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

то   вызов   функции    dequeue    блокируется   на   элементе   списка  tail->value,  так  как  при  добавлении  в  конец  списка  сообщения  (tail->value=msg)  в  списке  будет  прочитан  указатель  на  следующий элемент (tail->next) прежде, чем в нем будет установлена ссылка на недавно созданный элемент.

3.5.3  Взаимное исключение

Переменная типа sync  позволяет синхронизировать передачу данных от генератора потребителю. Однако в некоторых задачах встречается необходимость не синхронизированной передачи данных потребителю от нескольких производителей. При этом возникает опасность одновременного обращения к одной и той же структуре нескольких процессов. Поэтому необходим механизм, который бы гарантировал не синхронизированное обращение к данным очереди одновременно лишь одному процессу.

Механизм взаимного исключения (Mutual Exclusion – mutеx) в СС++ реализуется с помощью ключевого слова atomic (неделимый). Функции, являющиеся членами объекта и объявленные неделимыми, не будут вклиниваться в выполнение любой другой неделимой функции того же самого объекта.

Например, чтобы позволить множеству генераторов данных добавлять данные в конец одной и той же очереди, необходимо в объявление функции, поставляющей данные, вставить ключевое слово atomic следующим образом:

atomic void Queue::enqueue(int msg)

{

tail->next  = new IntQData;

tail->value = msg;

tail        = tail->next;

}

Такое определение гарантирует, что, даже если производители данных попытаются добавлять данные в конец к той же самой очереди одновременно, то фактически будет происходить постановка операций в очередь. Операции при этом будут выполняться в некотором последовательном порядке.

3.5.4  Функции передачи данных

В классе ios С++ для передачи от файла к файлу данных точно установленного типа в библиотеке <iostream.h> определены инфиксные операторы  <<  и  >> , вставляемые в определения следующим образом:

ostream& operator<<(ostream&, const TYPE& obj_in);

istream& operator>>(istream&, TYPE& obj_out);

С помощью этих форм описываются процессы записи и чтения не только для простых, предопределенных типов, но и для определяемых программистом комплексных типов, что расширяет диапазон модульности.

CC++ использует аналогичный механизм для взаимодействующих структур данных, расположенных в различных процессорных объектах. С каждым типом данных CC++ ассоциирует две функции передачи данных, которые определяют, как переместить тот или иной тип данных другому процессорному объекту. Функция

CCVoid& operator<<(CCVoid&, constTYPE& obj_in);

определяет, как объект TYPE должен быть упакован для связи. По указанному типу перемещаемых данных компилятор автоматически распознает, какого типа параметры и какой тип возвращаемого значения должны быть установлены, чтобы выполнить запрос на RPC. Точно так же функция приема

CCVoid& operator>>(CCVoid&, TYPE& obj_out);

определяет, как объект TYPE должен быть распакован. Определяется это на момент трансляции, когда объект TYPE принимается от другого процессорного объекта. После завершения запроса RPC obj_out будет копией obj_in, использованного в исходном процессорном объекте как параметр у функции передачи operator<<.

Рисунок 3.6 поясняет действие функций передачи определенного пользователем типа данных DVector с одного процессорного объекта на другой.

Текст 3.7. – Обмен данными с помощью функций из iostream

class DVector

{

int length;    double *elements;

friend CCVoid& operator<<(CCVoid&, const DVector&);

friend CCVoid& operator>>(CCVoid&, DVector&);

friend ostream& operator<<(ostream&, const DVector&);

friend istream& operator>>(istream&, DVector&);

};

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

{

v << input.length;                       // Отсылка значения length

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