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

В тексте 3.13 приведен фрагмент СС++ кода конечно-разностных вычислений в системе SPMD. Инициализация, создание процессорных объектов и глобальных указателей, необходимых для связи, условная циклическая конструкция многократно вызывающая некоторую подпрограмму finite_difference и глобальная переменная global_maximum, участвующая в вычислениях (описана вне фрагмента). Фрагмент реализует параллельную структуру вычислений в соответствии с рисунком 3.8.а

Набросок альтернативного кода СС++, выполняющего в SPMD код конечных разностей, представлен текстом 3.14. В этой версии, каждый процессор выполняет подпрограмму node_execute, осуществляя SPMD вычисления в соответствии с рисунком 3.8.б.

Текст 3.14: Вариант кода для размещения по рисунку 3.8.б

// Каждая задача для вычисления использует один и тот же код

void node_execute()

{

while(!done)

{

finite_difference(localgrid, locamax);

global_maximum(localmax; globmax);

if (globmax < threshold) done = TRUE;

}

}

void main(int argc, char *argv[])

{

intP = atoi(argv[1]);

// Инициализация, создающая массив процессорных объектов, узлов

initialize(P);

// Выполнение вычислений в процессорных объектах

parfor (int  i=0;   i<P;   i++)

nodes[i] ->node_execute();

}

3.6  Канальная библиотека

В приведенном ниже тексте 3.15 показан пример выполнения библиотеки канала, объекты которой были использованы ранее в тексте 3.3. Этот пример даст представление о том, как языковые конструкции и прочие парадигмы программирования СС++ можно использовать при разработке библиотечных подпрограмм, например, канальной связи в частности.

Текст 3.15: Пример библиотеки канала связи

classChannel

{

public:

Channel(ChannelUser *global, ChannelUser *global);

InPort *global get_in_port() { return in_port; }

OutPort *global get_out_port() { return out_port; }

private:

InPort *global in_port;

OutPort *global out_port;

};

// Выборочные функции класса Channel

Channel::Channel(ChannelUser *global out_pobj,                           ChannelUser *global in_pobj)

{

in_port = in_pobj->create_inport();

out_port = out_pobj->create_outport(in_port);

}

global class ChannelUser

{

public:

OutPort *global create_outport(InPort *global ip)

{ return new OutPort(ip); }

InPort *global create_inport()

{ return new InPort; }

};

class InPort: Queue

{

friend class OutPort;     //Разрешить OutPort стать в очередь

public:

int receive() { return dequeue(); }

};

class  OutPort

{

public:

OutPort(InPort *global iport) { inport = iport; }

void send(int val) { inport->enqueue(val); }

private:

InPort *global inport;

};


Рисунок 3.9. Схема использования функций библиотеки канала.

В приведенном программном фрагменте определяется процессорный объект ChannelUser и два класса каналов InPort и OutPort: канал для передачи и канал для приема. Этот глобального типа процессорный объект поставляет функции create_inport и create_outport, которые создают inport и outport. Любая программа, желающая использовать библиотеку канала, должна включить этот процессорный объект в качестве базового класса. Пример того, как это делается, можно посмотреть в тексте 3.3 при определении процессорного объекта Construction.

Спецификатор класса Channel объявляет три общих функции члена: конструктор канала, предназначенный для создания нового канала, который свяжет два точно установленных процессорных объекта; get_out_port, который возвращает указатель на каналoutport; и get_in_port, который возвращает указатель на канал inport.

Класс InPort получен из класса Queueтекста 3.6. Он добавляется к функциям, уже определенным в том классе. Новая функция просто убирает сообщение из очереди. Класс OutPort формирует глобальный указатель и определяет функцию, которая вызывает операцию постановки сообщения в очередь, упомянутую этим указателем.

3.7  Вопросы для самоконтроля

1.  Какие специальные объекты были определены в языке С++, превратив его в универсальный язык программирования параллельных процессов?