Knigi-for.me

Параллельное и распределенное программирование на С++ - Хьюз Камерон

Тут можно читать бесплатно Параллельное и распределенное программирование на С++ - Хьюз Камерон. Жанр: Прочая околокомпьтерная литература издательство , год . Так же Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте knigi-for.me (knigi for me) или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.

Теперь мы можем использовать такой встроенный интерфейсный класс, как mutex, в любых других пользовательских классах, предназначенных для безопасной обработки потоков выполнения. Предположим, мы хотели бы создать очередь с многопоточной поддержкой и многопоточный класс pvm_stream. Очередь будем использовать для хранения поступающих событий для множества потоков, образованных в программе. На некоторые потоки возложена ответственность за отправку сообщений различным PVM-задачам. PVM-задачи и потоки выполняются параллельно. Несколько потоков выполнения разделяют единственный PVM-класс и единственную очередь событий. Отношения между потоками, PVM-задачами, очередью событий и классом pvm_stream показаны на рис. 11.1.

Очередь, показанная на рис. 11.1, представляет собой критический раздел, поскольку она совместно используется несколькими выполняемыми потоками. Класс pvm_stream — это также критический раздел и по той же причине. Если эти критические разделы не синхронизировать и не защитить, то данные в очереди и классе pvm_stream могут разрушиться. Тот факт, что несколько потоков могут одновременно обновлять либо очередь, либо код класса pvm_stream, открывает среду для «гонок». Чтобы не допустить этого, мы должны обеспечить нашу очередь и к л асс pvm_stream встроенны м и средства м и блокировки и разблокировки. Эти средства также поддерживаются классом mutex. На рис. 11.2 показана диаграмма классов для наших пользовательских классов x_queue и pvm_stream.

Обратите внимание на то, что класс x_queue содержит к л асс мьютекс, т.е. между классами x_queue и мьютекс существует отношение агрегирования. Любая операция, которая изменяет состояние наше г о к л асса x_queue, может привести к «гонкам» данных, если, конечно, эгу операцию не синхронизировать. Следовательно, операции, которые добавляют объект в очередь или удаляют его из нее, являются кандидатами для синхронизации. В листинге 11.3 приведено объявление к л асса x_queue как шаблонного.

Рис.11.1. Отношения между потоками, PVM-задачами, очередью событий и классом pvm_stream в PVM-программе

Рис.11.2. Диаграмма классов для пользовательских классов x_queue и pvm_stream

// Листинг 11.3. Объявление класса x_queue

template <class T> x_queue class{

protected:

queue<T> EventQ;

mutex Mutex;

//...

public:

bool enqueue(T Object);

T dequeue(void);

//...

};

Метод enqueue() используется для добавления элементов в очередь, а метод dequeue() — для удаления их из очереди. Каждый из этих методов рассчитан на использование oбъeктaMutex. Определение этих методов приведено в листинге 11.4.

// Листинг 11.4. Определение методов enqueue() и dequeue()

tempIate<class T> bool x_queue<T>::enqueue(T Object)

{

Mutex.lock(); EventQ.push(Object); Mutex.unlock();

}

Leinplr.te<class T> T x_queue<T>::dequeue(void)

{

T Object; //. . .

Mutex.lock();

Object = EventQ.front()

EventQ.pop();

Mutex.unlock() ;

//. . .

return(Object);

}

Теперь очередь может функционировать (принимать новые элементы и избавляться от ненужных) в многопоточной среде. ПотокВ (см. рис.11.1) добавляет элементы в очередь, а потокА удаляет их оттуда. Класс mutex является интерфейсным классом. Он заключает в оболочку функции pthread_mutex_lock (), pthread_mutex_unlock (), pthread_mutex_init() и pthread_mutex_trylock(). Класс x_queue также является интерфейсным, поскольку он адаптирует интерфейс для встроенного класса queue<T> . Прежде всего, он заменяет интерфейсы методов push() и pop() методами enqueue() и dequeue() . При этом операции вставки и удаления элементов из очереди заключаются между вызовами методов Mutex.lock() и Mutex.unlock(). Поэтому в первом случае мы используем интерфейсный класс для инкапсуляции переменных типа pthread_mutex_t* и pthread_mutexattr_t*, а также заключаем в интерфейсную оболочку несколько функций из библиотеки Pthread. А во втором случае мы используем интерфейсный класс для адаптации интерфейса класса queue<T>. Еще одно достоинство класса mutex состоит в том, что его легко использовать в других классах, которые содержат критические разделы или области.

Класс 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<char *>(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, атакже стандартные функции использования семафоров и разделяемой памяти как часть единого решения. Все эти библиотеки и функции имеют собственные протоколы и синтаксис. Но у них есть много общего. Поэтому мы можем использовать интерфейсные классы, наследование и полиморфизм для создания упрощенного и непротиворечивого интерфейса, с которым непосредственно будет работать программист. Мы можем также скрыть от наших приложений детали реализации конкретной библиотеки. Если приложение опирается только на методы, используемые в наших интерфейсных классах, то оно будет защищено от изменений, вносимых в реализацию функций, обновлений библиотек и прочих «подводных» реструктуризации. В конце концов, работа над интерфейсом (интерфейсными классами) с компонентами параллелизма и библиотеками функций позволит существенно понизить уровень сложности параллельного программирования. Итак, рассмотрим подробнее, какие методы разработки интерфейсных классов можно реализовать для поддержки параллелизма.


Хьюз Камерон читать все книги автора по порядку

Хьюз Камерон - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-for.me.

Все материалы на сайте размещаются его пользователями.
Администратор сайта не несёт ответственности за действия пользователей сайта..
Вы можете направить вашу жалобу на почту knigi.for.me@yandex.ru или заполнить форму обратной связи.