ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 02.04.2021
Просмотров: 275
Скачиваний: 1
208
Глава 5. Синхронизация параллельно выполняемых задач
//
Листинг
5.4.
ФС
-
отношения
синхронизации
между
//
двумя
потоками
//...
float Number;
pthread_t ThreadA,ThreadB;
pthread_mutex_t Mutex,EventMutex;
pthread_cond_t Event;
void *worker1(void *X)
{
for(int Count = 1;Count < 100;Count++){
pthread_mutex_lock(&Mutex);
Number++;
pthread_mutex_unlock(&Mutex);
cout << "worker1:
число
равно
" << Number << endl;
if(Number == 50){
pthread_cond_signal(&Event);
}
}
cout << "
Выполнение
функции
worker1
завершено
." << endl;
return(0);
}
void *worker2(void *X)
{
pthread_mutex_lock(&EventMutex);
pthread_cond_wait(&Event,&EventMutex);
pthread_mutex_unlock(&EventMutex);
for(int Count = 1;Count < 50;Count++){
pthread_mutex_lock(&Mutex);
Number = Number + 20;
pthread_mutex_unlock(&Mutex);
cout << "worker2:
число
равно
" << Number << endl;
}
cout << "
Выполнение
функции
worker2
завершено
." << endl;
return(0);
}
int main(int argc, char *argv[])
{
pthread_mutex_init(&Mutex,NULL);
pthread_mutex_init(&EventMutex,NULL);
pthread_cond_init(&Event,NULL);
pthread_create(&ThreadA,NULL,worker1,NULL);
pthread_create(&ThreadB,NULL,worker2,NULL);
//...
return (0);
}
В листинге 5.4 показан пример реализации ФСотношений синхронизации. Поток
ThreadA
не может завершиться до тех пор, пока не стартует поток
ThreadB
. Если
значение переменной
Number
станет равным
50
, поток
ThreadA
сигнализирует об
этом потоку
ThreadB
. Теперь он может продолжать выполнение до самого конца.
Поток
ThreadB
не может начать выполнение до тех пор, пока не получит сигнал от
потока
ThreadA
. Поток
ThreadB
использует объект
EventMutex
вместе с условной
переменной
Event
. Объект
Mutex
используется для синхронизации доступа для запи
си значения разделяемой переменной
Number
. Для синхронизации различных событий
и доступа к критическим разделам задача может использовать несколько мьютексов.
5.3. Что такое семафоры
209
Пример реализации ФФотношений синхронизации показан в листинге 5.5.
//
Листинг
5.5.
ФФ
-
отношения
синхронизации
между
//
двумя
потоками
//...
float Number;
pthread_t ThreadA,ThreadB;
pthread_mutex_t Mutex,EventMutex;
pthread_cond_t Event;
void *worker1(void *X)
{
for(int Count = 1;Count < 10;Count++){
pthread_mutex_lock(&Mutex);
Number++;
pthread_mutex_unlock(&Mutex);
cout << "worker1:
число
равно
" << Number << endl;
}
pthread_mutex_lock(&EventMutex);
cout << "
Функция
worker1
в
состоянии
ожидания
. "
<< endl;
pthread_cond_wait(&Event,&EventMutex);
pthread_mutex_unlock(&EventMutex);
return(0);
}
void *worker2(void *X)
{
for(int Count = 1;Count < 100;Count++){
pthread_mutex_lock(&Mutex);
Number = Number * 2;
pthread_mutex_unlock(&Mutex);
cout << "worker2:
число
равно
" << Number << endl;
}
pthread_cond_signal(&Event);
cout << "
Функция
worker2
послала
сигнал
" << endl;
return(0);
}
int main(int argc, char *argv[])
{
pthread_mutex_init(&Mutex,NULL);
pthread_mutex_init(&EventMutex,NULL);
pthread_cond_init(&Event,NULL);
pthread_create(&ThreadA,NULL,worker1,NULL);
pthread_create(&ThreadB,NULL,worker2,NULL);
//...
return (0);
}
В листинге 5.5 поток
ThreadA
не может завершиться до тех пор, пока не завер
шится поток
ThreadB
. Поток
ThreadA
должен выполнить цикл 10 раз, а
ThreadB
—
100. Поток
ThreadA
завершит выполнение своих итераций раньше
ThreadB
, но будет
ожидать до тех пор, пока поток
ThreadB
не просигналит о своем завершении.
СС и СФотношения синхронизации невозможно реализовать подобным образом.
Эти методы используются для синхронизации порядка выполнения процессов.
210
Глава 5. Синхронизация параллельно выполняемых задач
5.4. Объектно-ориентированный подход
к синхронизации
Одно из преимуществ объектноориентированного программирования состоит
в защите, которую обеспечивает инкапсуляция компонентов данных объекта. Инкап
суляция может обеспечить для пользователя объектов “стратегии доступа к объектам
и принципы их применения” [24]. В примерах, представленных в этой главе, за при
меняемые стратегии доступа вся ответственность возлагалась на пользователя дан
ных. С помощью объектов и инкапсуляции ответственность можно переложить
с пользователя данных на сами данные. При таком подходе создаются данные, кото
рые, в отличие от функций, являются безопасными для потоков.
Для реализации такого подхода данные многопоточного приложения (по возмож
ности) необходимо инкапсулировать с помощью C++конструкций
class
или
struct
.
Затем инкапсулируйте такие механизмы синхронизации, как семафоры, блокировки
для обеспечения чтениязаписи и мьютексы событий. Если данные или механизмы
синхронизации представляют собой объекты, создайте для них интерфейсный класс.
Наконец, объедините объект данных с объектами синхронизации посредством насле
дования или композиции, чтобы создать объекты данных, которые будут безопасны
для потоков. Этот подход подробно рассматривается в главе 11.
5.5. Резюме
Для координации порядка выполнения процессов и потоков (
синхронизация задач
),
а также доступа к разделяемым данным (
синхронизация данных
) можно использовать
различные механизмы синхронизации. Существует четыре основных вида отношений
синхронизации задач. Отношение вида “стартстарт” (СС) означает, что задача A не
может начаться до тех пор, пока не начнется задача B. Отношение вида “финиш
старт” (ФС) означает, что задача A не может завершиться до тех пор, пока не начнет
ся задача B. Отношение вида “стартфиниш” (СФ) означает, что задача A не может на
чаться до тех пор, пока не завершится задача B. Отношение вида “финишфиниш”
(ФФ) означает, что задача A не может завершиться до тех пор, пока не завершится за
дача B. Для реализации этих отношений синхронизации задач можно использовать
условную переменную типа
pthread_cond_t
, которая определена стандартом POSIX.
Для описания синхронизации данных используются некоторые типы алгоритмов
модели PRAM. Стратегию доступа EREW (исключающее чтение и исключающая за
пись) можно реализовать с помощью мьютексного семафора. Мьютексный семафор
защищает критический раздел, обеспечивая последовательный вход в него. Эта стра
тегия разрешает либо доступ для чтения, либо доступ для записи. Стандарт POSIX оп
ределяет мьютексный семафор типа
pthread_mutex_t
, который можно использо
вать для реализации стратегии доступа EREW. Чтобы реализовать стратегию доступа
CREW (параллельное чтение и исключающая запись), можно использовать блокиров
ки чтениязаписи. Стратегия доступа CREW описывает возможность удовлетворения
множества запросов на чтение, но при монопольной записи данных. Стандарт POSIX
определяет объект блокировки для обеспечения чтениязаписи типа
pthread_rwlock_t
, а объектноориентированный подход к синхронизации данных
позволяет встроить механизм синхронизации в объект данных.