Камерон Хьюз - Параллельное и распределенное программирование на С++
- Название:Параллельное и распределенное программирование на С++
- Автор:
- Жанр:
- Издательство:Издательский дом «Вильямс»
- Год:2004
- Город:МоскваСанкт-ПетербургКиев
- ISBN:ISBN 5-8459-0686-5 (рус.)ISBN 0-13-101376-9 (англ.)
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Камерон Хьюз - Параллельное и распределенное программирование на С++ краткое содержание
Эта книга адресована программистам, проектировщикам и разработчикам программных продуктов, а также научным работникам, преподавателям и студентам, которых интересует введение в параллельное и распределенное программирование с использованием языка С++.
Параллельное и распределенное программирование на С++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Класс pvm_stream (см. рис. 11 1) также является критическим разделом, поскольку оба потока выполнения (А и В) имеют доступ к потоку данных. Опасность возникновения «гонок» данных здесь вполне реальна, поскольку потокА и поток В могут получить доступ к потоку данных одновременно. Следовательно, мы используем класс mutex в нашем классе pvm_stream для обеспечения необходимой синхронизации.
// Листинг 11.5. Объявление класса pvm_stream
class pvm_stream{
protected:
mutex Mutex;
int TaskId;
int MessageId;
// . - -
public:
pvm_stream & operator <<(string X);
pvm_stream & operator «(int X);
pvm_stream &operator <<(float X);
pvm_stream &operator>>(string X);
//.. .
};
Как и в классе x_queue,объект Mutexиспользуется применительно к функциям, которые могут изменить состояние объекта класса pvm_stream.Например, мы могли определить один из операторов "«" следующим образом .
// Листинг 11.6. Определение оператора << для
// класса pvm_stream
pvm_stream &pvm_stream::operator<<(string X) {
//...
pvm_pkbyte(const_cast(X.data()),X.size(),1);
Mutex.lock();
pvm_send(TaskId,MessageId);
Mutex.unlock();
//.. .
return(*this);
}
Класс pvm_streamиспользует объекты Mutexдля синхронизации доступа к его критическому разделу точно так же, как это было сделано в классе x_queue.Важно отметить, что в обоих случалх инкапсулируются pthread_mutex-функции .Программист не должен беспокоиться о правильном синтаксисе их вызова. Здесь также используется более простой интерфейс для вызова функций lock () и unlock (). Более того, здесь нельзя перепутать, какую pthread_mutex_t*-nepeмeннyю нужно использовать с pthread_mutex-функциями. Наконец, программист может объявить несколько экземпляров класса mutex, не обращалсь снова и снова к функциям библиотеки Pthread. Раз мы сделали ссылку на Pthread-функции в определениях методов клlacca mutex, то теперь нам достаточно вызывать только эти методы.
Подробнее об объектно-ориентированном взаимном исключении и интерфейсных классах
Чтобы справиться со сложностью написания и поддержки программ с параллелизмом, попробуем упростить API-интерфейс с соответствующими библиотеками. В некоторых системах, возможно, имеет смысл создать библиотеки Pthreads, MPI, атакже стандартные функции использования семафоров и разделяемой памяти как часть единого решения. Все эти библиотеки и функции имеют собственные протоколы и синтаксис. Но у них есть много общего. Поэтому мы можем использовать интерфейсные классы, наследование и полиморфизм для создания упрощенного и непротиворечивого интерфейса, с которым непосредственно будет работать программист. Мы можем также скрыть от наших приложений детали реализации конкретной библиотеки. Если приложение опирается только на методы, используемые в наших интерфейсных классах, то оно будет защищено от изменений, вносимых в реализацию функций, обновлений библиотек и прочих «подводных» реструктуризации. В конце концов, работа над интерфейсом (интерфейсными классами) с компонентами параллелизма и библиотеками функций позволит существенно понизить уровень сложности параллельного программирования. Итак, рассмотрим подробнее, какие методы разработки интерфейсных классов можно реализовать для поддержки параллелизма.
«Полуширокие» интерфейсы
Базовый POSIX-семафор используется для синхронизации доступа к критическому разделу нескольких процессов, а базовый POSIX -поток— для синхронизации доступа к критическому разделу нескольких потоков. В обоих случалх используются переменные синхронизации и ряд функций, работающих с этими переменными. Библиотеки MPI и PVM содержат примитивы передачи сообщений и обладают средствами порождения задач. Но интерфейсы этих библиотек различны. Нетрудно предположить, что работа прикладного программиста была бы эффективней, если бы он сосредоточил свое внимание на логике и структуре программы. Однако там, где семантика программы теряет свою ясность из-за необходимости использовать библиотеки, в которых попадаются аналогичные функции, а сами библиотеки отличаются синтаксисом и протоколами, у программиста возникают немалые трудности. Отсюда вытекает потребность универсализации интерфейса, который бы подходил для работы с разными библиотеками.
Существует по крайней мере два подхода к разработке общего интерфейса для семейства, или коллекции классов. Объектно-ориентированный подход начинается с общего и переходит к частностям посредством наследования. Другими словами, возьмем минимальный набор характеристик и атрибутов, которыми должен обладать каждый член рассматриваемого сехмейства классов, а затем посредством наследования будем конкретизировать характеристики для каждого класса. При таком подходе по мере «спуска» по иерархии классов интерфейс становится все более «узким». Второй подход часто используется в коллекциях шаблонов. Шаблонные методы начинаются c конкретного и переходят к более общему посредством «широких» интерфейсов. «Широкий» интерфейс включает обобщение всех характеристик и атрибутов (см. книгу Страуструпа « Язык программирования С++» , 1997). Если бы нам пришлось применить к библиотекам средств параллелизма «узкий» и «широкий» интерфейсы, то согласно метолу «узкого интерфейса» мы бы взяли от каждой библиотеки общие, или пересекающиеся, части (т.е. пересечение), обобщили их и поместили в базовый класс. И, наоборот, реализуя метод «широкого интерфейса», нужно было бы поместить в базовый класс все функциональные части каждой библиотеки (т.е. объединение), предварительно обобщив их. В результате пересечения мы получили бы меньший по объему да и менее полезный класс. А результат объединения, скорей всего, поразил бы каждого своей громоздкостью. Решение, которое интересует нас в данном случае, находится где-то посередине, т.е. нам нужны «полуширокие» интерфейсы. Начнем же мы с метода «узкого» интерфейса и обобщим его настолько, насколько это можно сделать в пределах иерархии одного класса. Затем используем этот «узкий» интерфейс в качестве основы для коллекции классов, которые связаны не наследованием, а функциями. «Узкий» интерфейс должен действовать в качестве стратегии сдерживания «ширины», до которой может разбухнуть «полуширокий» интерфейс. Другими словами, нам не нужно объединять буквально все характеристики и атрибуты; мы хотим получить объединение только тех частей, которые логически связаны с нашим «узким» интерфейсом. Проиллюстрируем эту мысль иа примере простого проекта интерфейсных классов для POSIX-семафора, Pthread-мьютекса и Pthread-переменной блокировки.
Читать дальшеИнтервал:
Закладка: