Параллельное и распределенное программирование на С++
Шрифт:
ostream_iterator<int> X(cout, «\n»);
будет создан объект X с использованием аргумента cout. Второй аргумент в конструкторе является разделителем, который автоматически будет размещаться после каждого int– значения, вставляемого в поток данных. Объявление итератора ostream_iterator выглядит следующим образом (листинг 11.22).
// Листинг 11.22. Объявление класса ostream_iterator
template <class _Tp> class ostream_iterator {
protected:
ostream* _M_stream;
const char* _M_string; public:
typedef output_iterator_tag iterator_category;
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
ostream_iterator(ostream& _s) : _M_stream(&_s),_M_string(0) {}
ostream_iterator(ostream& _s, const char* _с): _M_s tream (&_s) , _M_string (_с) { }
ostream_iterator<_Tp>& operator=(const _Tp& _value) {
*_M_stream << _value;
if (_M_string){
*_M_stream << _M_string;
return *this;
}
ostream_iterator<_Tp>& operator* { return *this; }
ostream_iterator<_Tp>& operator++ { return *this; }
ostream_iterator<_Tp>& operator++(int) { return *this; }
};
Конструктор
// Программа 11.2
10 int main(int argc, char *argv[])
11 {
12
13 int Size,Pid,Status,Fdl[2],Fd2[2];
14 pipe(Fdl); pipe(Fd2);
15 strstream Buffer;
16 char Value[50];
17 float Data;
18 vector<float>X(5,2.1221), Y;
19 Buffer « Fdl[0] « ends;
20 Buffer » Value;
21 setenv(«Fdin»,Value,l);
22 Buffer.clear;
23 Buffer « Fd2[l] « ends;
24 Buffer » Value;
25 setenv(«Fdout»,Value,l);
26 Pid = fork;
27 if(Pid != 0){
28 ofstream OPipe;
29 OPipe.attach(Fdl[l] ) ,-
30 ostream_iterator<float> OPtr(OPipe,"\n»);
31 OPipe « X.size « endl;
32 copy(X.begin,X.end,OPtr);
33 OPipe « flush;
34 ifstream IPipe;
35 IPipe.attach(Fd2[0]);
36 IPipe » Size;
37 for(int N = 0; N < Size;N++)
38 {
39 IPi ре » Data;
40 Y.push_back(Data);
41 }
42 wait(&Status);
43 ostream_iterator<float> OPtr2(cout,"\n»);
44 copy(Y.begin,Y.end,OPtr2);
45 OPipe.close;
46 IPipe.close;
47 }
48 else{
49 execl("./programll-2b»,«programll-2b»,NULL);
50 } 51
52 return(0);
53 }
В строках 21 и 25 системнал функция setenv используется для передачи значений файловых дескрипторов сыновнему процессу. Это возможно благодаря тому, что сыновний процесс наслелует среду родительского процесса. Мы можем устанавливать переменные среды в программе с помощью вызова функции setenv . В данном случае мы устанавливаем их следующим образом.
Fdin=filedesc; Fdout=filedesc;
Сыновний
ostream_iterator<float> OPtr(OPipe,"\n»);
Теперь итератор OPtr ссылается на объект OPipe. После каждой порции помещаемых в канал данных будет вставляться разделитель "\n». С помощью итератора OPtr мы можем поместить в канал любое количество float– значений. При этом мы можем связать с каналом несколько итераторов различных типов. Но в этом случае необходимо, чтобы на «считывающем» конце канала данные извлекались с использованием ите раторов соответствующих типов. При выполнении слелующей инструкции из программы 11.2 в канал сначала помещается количество элементов, подлежащих передаче: OPipe « X.size « endl;
Сами элементы отправляются с использованием одного из стандартных С++-алгоритмов:
copy(X.begin ,X.end ,OPtr) ;
Алгоритм copy копирует содержимое одного контейнера в контейнер, связанный с итератором приемника. Здесь итератором приемника является объект OPtr. Объект OPtr связан с объектом OPipe, поэтому при выполнении алгоритма copy («уместившегося» в одной строке кода) в канал переписывается все содержимое контейнера. Этот пример демонстрирует возможность использования стандартных алгоритмов для организации взаимодействия между различными частями сред параллельного или распределенного программирования. В данном случае алгоритм copy пересылает информацию от одного процесса другому (из одного адресного пространства в другое). Эти процессы выполняются параллельно, и алгоритм copy значительно упрощает взаимодействие между ними. Мы подчеркиваем важность этого подхода, поскольку, если есть хоть какал-то возможность упростить логику параллельной или распределенной программы, ею нужно непременно воспользоваться. Ведь межпроцессное взаимодействие — это один из самых сложных разделов параллельного или распределенного программирования. С++-алгоритмы, библиотека классов iostreamS и итератор типа ostream_iterator как раз и позволяют понизить уровень сложности разработки таких программ. Использование манипулятора flush (в строке 33) гарантирует прохождение данных по каналу.
В программе 11.2.1 сыновний процесс сначала получает количество объектов, принимаемых от канала (в строке 36), а затем для считывания самих объектов использует объект IPipe класса istream.
// Программа 11.2.1
11 class multiplier{
12 float X;
13 public:
14 multiplier(float Value) { X = Value;}
15 float &operator(float Y) { X = (X * Y);return(X);}
16 }; 17
18
19 int main(int argc,char *argv[])
20 {
21 char Value[50] ;