Обратите внимание на то, что классы istream и ostream наследуют класс ios . Класс ios поддерживает состояние потока данных и состояние буфера, используемого классами istream и ostream. Наш класс mios исполняет ту же роль в отношении класса pvm_stream. Классы istream и ostream содержат определения операторов "<<" и ">>". Эти же операторы определены и в нашем классе pvm_stream. Поэтому, хотя наш класс pvm_stream не связан с iostream-классами наследованием, между ними существует интерфейсная связь. Мы используем интерфейс iostream-классов в качестве «полуширокого» интерфейса для классов pvm_stream и mios. Обратите внимание на то, что класс mios (см. рис. 11.4) наслелуется классом pvm_stream. Если мы хотим поддерживать потоковое представление с помощью класса pvm_stream, то для этого как раз подходит понятие интерфейсного класса.
Рис. 11.4. Диаграмма классов, отображающая отношения между основными классами библиотеки iostream, и диаграмма класса pvm_stream
Перегрузка операторов "«" и "»" для PVM-потоков данных
Итак, рассмотрим определение операторов "«" и ">>" для класса pvm__stream. Оператор вставки (<<) используется для заключения в оболочку функций pvm_send () и pvm_pk. Вот как выглядит определение этого операторного метода.
// Листинг 11.13. Определение оператора "<<" для класса
// pvm_stream class
pvm_stream &pvm_stream::operator<<(int Data) {
//...
reset();
pvm_pkint(&Data,1,1); pvm_send(TaskId,MessageId); //.. .
return(*this);
}
Подобное определение существует для каждого типа данных, которые будут обрабатываться с использованием класса pvm_stream. Метод reset () унаследован от класса mios. Этот метод используется для инициализации буфера отправки д анных. TaskId и MessageId — это члены данных класса pvm_stream, которые устанавливаются с помо щ ью мето д ов taskId( ) и messageId( ). Определяемый здесь оператор вставки позволяет отправлять данные PVM-задаче с помощью стандартной записи операции вывода в поток.
int Value = 2004;
pvm_stream MyStream;
//...
MyStream << Value;
//.. .
Оператор извлечения данных (>>) используется подобным образом, но для получения сообщений от PVM-задач. В действительности оператор ">>" заключает в оболочку функции pvm_recv () и pvmupk (). Определение этого операторного м етода выглядит так.
// Листинг 11.14. Определение оператора для класса
// pvm_stream
pvm_stream &pvm_stream::operator>>(int &Data) {
int BufId;
//. . .
BufId = pvm_recv(TaskId,MessageId);
StreamState = pvm_upkint(&Data,l,l); //.. .
return(*this);
}
Этот тип определения позволяет получать сообщения от PVM-задач с помощью оператора извлечения данных.
int Value;
pvm_stream MyStream;
MyStream >> Value;
Поскольку каждый из рассмотренных операторных методов возвращает ссылку на тип pvm_stream , операторы вставки и извлечения можно соединить в цепочку.
Mystream << Valuel << Value2;
Mystream >> Value3 >> Value4;
Используя этот простой синтаксис, программист изолирован от более громоздкого синтаксиса функций pvm_send, pvm_pk, pvm_upk и pvm_recv . При этом он работает с более знакомыми для него объектно-ориентированными потоками данных. В данном случае поток данных представляет буфер сообщений, а элементы, которые помещаются в него или извлекаются оттуда, представляют сообщения, которыми обмениваются между собой PVM-процессы. Вспомните, что каждый PVM-процесс имеет отдельное адресное пространство. Поэтому операторы "<<" и ">>" не только маскируют вызовы функций pvm_send и pvm_recv, они также маскируют заложенную в них организацию связи. Поскольку класс pvm_stream можно использовать в много-поточной среде, операторы вставки и извлечения данных должны обеспечивать безопасность потоков выполнения.
Класс pvm_stream (см. рис. 11.4) содержит класс mutex. Класс mutex можно использовать для защиты критических разделов, которые имеются в классе pvm_stream. Класс pvm_stream инкапсулирует доступ к буферу отправки и буферу приема данных. Взаимодействие потоков выполнения и класса pvm_stream с буферами pvm_send и pvm_receive показано на рис. 11.5.
Рис.11.5. Взаимодействие потоков выполнения и класса pvm_stream с буферами pvm_send и pvm_receive
Критическими разделами являются не только буферы отправки и приема данных. Класс mios, используемый для хранения состояния класса pvm_stream, также является критическим разделом. Для защиты этого компонента можно использовать класс mutex.
При обращении к операторам вставки и извлечения данных можно использовать объект Mutex.
// Листинг 11.15.
//Определение операторов «<<» и «>>» для класса pvm_stream
pvm_stream &pvm_stream::operator<<(int Data) {
//.. .
Mutex.lock(); reset();
pvm_pkint(&Data,1,1); pvm_send(TaskId,MessageId); Mutex.unlock(); //.. .
return(*this);
}
pvm_stream &pvm_stream::operator>>(int &Data) {
int BufId; //. . .
Mutex.lock();
BufId = pvm_recv(TaskId,MessageId);
StreamState = pvm_upkint(&Data,1,1);
Mutex.unlock();
//. . .
return(*this);
}
Этот вид защиты позволяет сделать класс pvm_stream безопасным. Здесь мы не представили код обработки исключений или другой код, который бы позволил предотвратить бесконечные отсрочки или взаимную блокировку. Основнал идея в данном случае — сделать акцент на компонентах и вариантах архитектуры, которые пригодны для поддержки параллелизма. Интерфейсный класс mutex и класс pvm_stream можно использовать многократно, и оба они поддерживают параллельное программирование. Предполагается, что объекты класса pvm_stream должны использоваться PVM-задачами при отправке и приеме сообщений. Но это не является жестким требованием. Для того чтобы пользователь мог применить концепцию класса pvm_stream к своим классам, для них необходимо определить операторы вставки (<<) и извлечения (>>).
Пользовательские классы, создаваемые для обработки PVM-потоков данных
Чтобы понять, как определенный пользователем класс можно использовать совместно с классом pvm_stream, попробуем усовершенствовать возможности PVM-палитры, представленной в главе 6. Класс палитры представляет простую коллекцию цветов. Для удобства будем сохранять цвета в векторе строк (vector<string>) с именем Colors.
Начне м с объявления класса spectral_palette, который содержит friend- объявления дл я операторов вставки (<<) и извлечени я (>>).
// Листинг 11.16. Объявление класса spectral_palette
class spectral_palette : public pvm_object{
protected:
//. . .
vector<string> Colors;
public:
spectral_palette(void);
//...
friend pvm_stream &operator>>(pvm_stream &In,spectral_palette &Obj);
friend pvm_stream &operator<<(pvm_stream &Out,spectral_palette &Obj);