Параллельное программирование: Учебное пособие, страница 64

v << input.elements[i];    // Отсылка i-того значения массива

return v;

}

ostream& operator<<(ostream& v, const DVector& input)

{

v << input.length;                       // Запись значения length

for (int i=0; i<input.length; i++)

v << input.elements[i];       // Записьi-того значения массива

return v;

}

CCVoid& operator>>(CCVoid& v, DVector& output)

{

v >> output.length;                     // Прием значения length

output.elements = new double[output.length];

for (int i=0; i<output.length; i++)

v >> output.elements[i];     // Прием i-того значения массива

return v;

}

istream& operator>>(istream& v, DVector& output)

{

v >> output.length;                     // Чтение значения length

output.elements = new double[output.length];

for (int i=0; i<output.length; i++)

v >> output.elements[i];     // Чтение i-го значения массива

return v;

}

Рисунок 3.6: Использование функций передачи данных.

На рисунке 3.6 образец сообщения V, тип DVector которого определен пользователем, с помощью глобального указателя gp принимается на процессорном объекте  pobj2  благодаря ссылке  gp->transfer(V) в процессорном объекте  pobj1 .

Функция  <<  упаковывает структуру данных в источнике, а функция  >>  – распаковывает ее у получателя.

Тип CCVoid – определенный транслятором тип, аналогичный типам istream и ostream, использованному в iostream библиотеке. Для простых типов функции передачи данных генерируются транслятором CC++ автоматически. Для локальных указателей, массивов и структур, которые включают локальные указатели, функции передачи данных должны быть созданы программистом. Например, в тексте 3.7 показана передача данных и функции <iostream.h> для типа DVector, включающего двумерный вектор. Эти функции посылают длину вектора, сопровождаемую векторными компонентами, и получают эти значения в том же самом порядке. Ключевое словоfriend в спецификации C++ используют для имен функций не членов класса, которые, однако, могут обращаться к частным переменным класса. Определив эти функции передачи данных, можно (см. рис. 3.6) делать RPC с параметром DVector, который сформирован следующим образом:

DVector V;le[2];

V.length = 2;

V.elements[0] = 42.0;

V.elements[1] = 17.0;

gp->transfer(V);

3.5.5  Асинхронная передача данных

Потребность в асинхронной связи может возникать тогда, когда вычислительные задачи должны обратиться к элементам структуры общих (разделяемых) данных неструктурированным способом (Раздел 2.3.4). Это требование может быть удовлетворено в CC++ тремя различными способами:

1)  Структура разделяемых данных может быть включена в специализированный набор данных одновременно выполняемых задач, ориентированных на чтение или требование записи с использованием операций канала.

2)  Структура разделяемых данных может быть распределена среди выполняемых задач. В этом случае каждая вычислительная задача в ожидании запросов на передачу должна периодически список сообщений опрашивать (poll голосовать). Так, для поддержки операции опроса в тексте 3.6 достаточно расширить класс Queue, поместив в структуру IntQData добавочное поле, принимающее значения  full  или  empty. Значение empty устанавливается, когда созданный элемент списка еще пуст, а full – когда значения данных помещены в список элементов. Функция опроса заключается в проверке состояния этого поля у элемента списка в голове очереди.

3)  Третий вариант обращения к распределенным структурам разделяемых задачами данных непосредственно использует механизм RPC языка CC++. Прежде, чем сообщение будет послано в канал, задача обращается к данным другого процессорного объекта посредством RPC путем вызова соответствующей функции-члена. Простым примером записи с использованием удаленного вызова процедуры (RPC) является вызов функции  get_x  в тексте 3.1.

3.5.6  Детерминизм