Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Return(ReturnValue);






}

Благодаря инкапсуляции мы также защищаем переменные типа pthread_mutex_t * и pthread_mutexattr_t *. Другими словами, при вызове методов lock(), unlock(), trylock() и других нам не нужно беспокоиться о том, к каким мьютексным переменным или переменным атрибутов будут применены эти функции. Возможность скрывать информацию (посредством инкапсуляции) позволяет программисту писать вполне безопасный код. С помощью свободно распространяемых версий Рthread-функций этим функциям можно передать любую переменную типа pthread_mutex_t. Однако при передаче одной из этих функций неверно заданного мьютекса может возникнуть взаимоблокировка или отсрочка бесконечной длины. Инкапсуляция переменных типа pthread_mutex_t и pthread_mutexattr_t в к л ассе mutex предостав л яет программисту полный контроль над тем, какие функции получат доступ к этим переменным.

Теперь мы можем использовать такой встроенный интерфейсный класс, как 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, то теперь нам достаточно вызывать только эти методы.

 






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.