ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.04.2021

Просмотров: 265

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

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

. Для синхронизации различных событий

и доступа к критическим разделам задача может использовать несколько мьютексов.


background image

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

 не просигналит о своем завершении.

СС и СФотношения синхронизации невозможно реализовать подобным образом.

Эти методы используются для синхронизации порядка выполнения процессов.


background image

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

, а объектноориентированный подход к синхронизации данных

позволяет встроить механизм синхронизации в объект данных.