Камерон Хьюз - Параллельное и распределенное программирование на С++
- Название:Параллельное и распределенное программирование на С++
- Автор:
- Жанр:
- Издательство:Издательский дом «Вильямс»
- Год:2004
- Город:МоскваСанкт-ПетербургКиев
- ISBN:ISBN 5-8459-0686-5 (рус.)ISBN 0-13-101376-9 (англ.)
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Камерон Хьюз - Параллельное и распределенное программирование на С++ краткое содержание
Эта книга адресована программистам, проектировщикам и разработчикам программных продуктов, а также научным работникам, преподавателям и студентам, которых интересует введение в параллельное и распределенное программирование с использованием языка С++.
Параллельное и распределенное программирование на С++ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Безотносительно к реализации деталей, операции блокировки, разблокировки и «пробной» блокировки являются характеристиками переменных синхронизации. Поэтому мы создадим базовый класс, который будет служить «трафаретом» для целого семейства классов. Объявление класса synchronization_variable представленовлистинге 11.7.
// Листинг 11.7. Объявление класса synchronization_variable
class synchronization_variable{
protected:
runtime_error Exception;
//.. .
public:
int virtual lock(void) = 0;
int virtual unlock(void) = 0;
int virtual trylock(void) = 0;
//.. .
}
;
Обратите внимание на то, что методы синхронизации класса synchronization_variable объявлены виртуальными и инициализированы значением 0. Это означает, что они являются чисто виртуальными методами, что делает класс synchronization_variable абстрактным. Из класса, который содержит одну или несколько чисто виртуальных функций, объект прямым путем создать нельзя. Чтобы использовать этот класс, необходимо вывести из него новый класс и определить в нем все чисто виртуальные функции. Абстрактный класс — это своего рода трафарет с указанием того, какие функции должны быть определены в производном классе. Он предлагает интерфейсный проект для производных классов. Он отнюдь не диктует, как нужно реализовать методы, он лишь отмечает, какие методы должны быть представлены в выведенном классе, причем они не могут оставаться в нем чисто виргуальными. С помощью имен этих методов мы можем подсказать предполагаемое их поведение. Таким образом, проектный интерфейсный класс предлагает проект без реализации. Класс этого типа используется в качестве фундамента для будущих классов. Проектный класс гарантирует, что интерфейс будет иметь определенный вид [9]. Класс synchronization_variable обеспечивает интерфейсный трафарет для ceмейства переменных синхронизации. Для обеспечения различных вариантов реализации интерфейса мы используем наследование. Pthread-мьютекс — прекрасный кандидат для интерфейсного класса, поэтому мы определяем класс mutex как производный от класса synchronization_variable.
// Листинг 11.8. Объявление класса мьютекс, который
// наследует класс synchronization_variable
class mutex : public synchronization_variable {
protected:
pthread_mutex_t *Mutex;
pthread_mutexattr_t *MutexAttr;
//.. .
public:
int lock(void) ;
int unlock(void);
int trylock(void);
//. . .
};
Класс mutex должен обеспечить реализации для всех чисто виртуальных функций. Если эти функции определены, значит, политика, предложеннал абстрактным классом, выдержана. Класс mutex теперь не является абстрактным, поэтому из него и из его потомков можно создавать объекты. Каждый из методов класса mutex заключает в оболочку соответствующую Pthread-функцию. Например, код
int mutex::trylock(void) {
//.. .
return(pthread_mutex_trylock(Mutex); //. . .
}
обеспечивает интерфейс для функции pthread_mutex_trylock(). Интерфейсные варианты функций lock(), unlock() и trylock() упрощают вызовы функций библиотеки Pthread. Наша цель — использовать инкапсуляцию и наследование для определения всего семейства мьютексных классов. Процесс наследования — это процесс специализации. Производный класс включает дополнительные атрибуты или характеристики, которые отличают его от предков. Каждый атрибут или характеристика, добавленная в производный класс, конкретизирует его. Теперь мы, используя наследование, можем спроектировать специализацию класса mutex путем введения понятия мьютексного класса, способного обеспечить чтение и запись. Наш обобщенный класс mutex предназначен для защиты доступа к критическому разделу. Если один поток заблокировал мьютекс, он получает доступ к критическому разделу, защищаемому этим мьютексом. Иногда такая мера предосторожности оказывается излишне суровой. Возможны ситуации, когда вполне можно разрешить доступ нескольких потоков к одним и тем же данным, если ни один из этих потоков не модифицирует данные. Другими словами, в некоторых случаях мы можем ослабить блокировку критического раздела и «намертво» запирать его только для действий, которые стремятся модифицировать данные, разрешал при этом доступ для действий, которые предполагают лишь считывание или копирование данных. Такой вид блокировки называется блокировкой считывания (read lock). Блокировка считывания позволяет параллельный доступ к критическому разделу для чтения данных. Критический раздел может быть уже заблокированным одним потоком, но другой поток также может получить блокировку, если у него нет намерения изменять данные. Критический раздел может быть заблокирован для записи одним потоком, а другой поток может запросить блокировку для чтения этого критического раздела.
Архитектура «классной доски» служит прекрасным примером структуры, которая может использовать преимущества «мьютексов считывания» и мьютексов более общего назначения. Под «классной доской» понимается область памяти, разделяемал параллельно выполняемыми задачами. «Классная доска» используется для хранения решений некоторой проблемы, которую совместными усилиями решает целая группа задач. По мере приближения задач к решению проблемы каждая из них записывает результаты на «классную доску» и просматривает ее содержимое с целью поиска результатов, сгенерированных другими задачами, которые могут оказаться полезными для нее. Структура «классной доски» является критическим разделом. В действительности мы хотим, чтобы одновременно только одна задача могла обновлять содержимое «классной доски». Однако ее одновременное считывание мы можем позволить любому количеству задач. Кроме того, если несколько задач уже считывает содержимое «классной доски», нам нужно, чтобы оно не начало обновляться до тех пор, пока все эти задачи не завершат чтение. «Мьютекс считывания» как раз подходит для такой ситуации, поскольку он может управлять доступом к «классной доске», разрешал его только считывающим задачам и запрещал его для записывающих задач. Но если решение проблемы будет найдено, содержимое «классной доски» необходимо обновить. В процессе обновления нам нужно, чтобы ни одна считывающал задача не получила доступ к критическому разделу. Мы хотим заблокировать доступ для чтения до тех пор, пока не завершит обновление записывающал задача. Следовательно, нам нужно создать «мьютекс записи». В любой момент времени удерживать этот «мьютекс записи» может только одна задача. Поэтому мы делаем различие между мьютексом, который блокируется для считывания, но не для записи, и мьютексом, который блокируется для записи, но не для считывания. С использованием мьютекса считывания у нас может быть несколько параллельных считывающих задач, а с использованием мьютекса записи — только одна записывающал задача. Описаннал схема является частью модели CREW (Concurrent Read Exclusive Write — параллельное чтение, монопольнал запись) параллельного программирования.
Читать дальшеИнтервал:
Закладка: