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

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

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

Добавлен: 02.04.2021

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

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

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

5.3. Что такое семафоры

203

Окончание табл. 5.5

Прототипы функций

Описание

int
 pthread_rwlockattr_setpshared
  (pthread_rwlockattr_t * attr,
   int pshared);

int
 pthread_rwlockattr_getpshared
  (const pthread_rwlockattr_t *
                restrict attr,
   int *restrict pshared);

Устанавливает или возвращает атрибут

process-shared

 атрибутного объекта бло

кировки чтениязаписи, заданного парамет
ром

 

attr

Параметр

 

pshared 

может содер

жать следующие значения:

PTHREAD_PROCESS_SHARED

(разрешает блокировку чтениязаписи, разде
ляемую любыми потоками, которые имеют дос
туп к памяти, выделенной для этого объекта
блокировки, даже если потоки принадлежат
различным процессам);

PTHREAD_PROCESS_PRIVATE

(блокировка чтениязаписи разделяется меж
ду потоками одного процесса)

5.3.3.1. Использование блокировок чтения-записи

для реализации стратегии доступа

Блокировки чтениязаписи можно использовать для реализации стратегии доступа

CREW (параллельное чтение и исключающая запись). Согласно этой стратегии воз
можность параллельно считывать данные может быть предоставлена сразу несколь
ким задачам, но только одна задача получит право доступа для записи. При выполне
нии монопольной записи в этом случае не будет дано разрешение на параллельное
чтение данных. Использование блокировок чтениязаписи для защиты критических
разделов продемонстрировано в листинге 5.3.

// 

Листинг

 5.3. 

Пример

 

использования

 

потоками

 

блокировок

//              

чтения

-

записи

//...
pthread_t ThreadA,ThreadB,ThreadC,ThreadD;
pthread_rwlock_t RWLock;

void *producer1(void *X)
{
   pthread_rwlock_wrlock(&RWLock);
   // 

Критический

 

раздел

.

   pthread_rwlock_unlock(&RWLock);
   return(0);
}

void *producer2(void *X)
{
   pthread_rwlock_wrlock(&RWLock);
   // 

Критический

 

раздел

.

   pthread_rwlock_unlock(&RWLock);
}


background image

204

Глава 5. Синхронизация параллельно выполняемых задач

void *consumer1(void *X)
{
   pthread_rwlock_rdlock(&RWLock);
   // 

Критический

 

раздел

.

   pthread_rwlock_unlock(&RWLock);
   return(0);
}

void *consumer2(void *X)
{
   pthread_rwlock_rdlock(&RWLock);
   // 

Критический

 

раздел

.

   pthread_rwlock_unlock(&RWLock);
   return(0);
}

int main(void)
{
   pthread_rwlock_init(&RWLock,NULL);
   // 

Устанавливаем

 

атрибуты

 

мьютекса

.

   pthread_create(&ThreadA,NULL,producer1,NULL);
   pthread_create(&ThreadB,NULL,consumer1,NULL);
   pthread_create(&ThreadC,NULL,producer2,NULL);
   pthread_create(&ThreadD,NULL,consumer2,NULL);
   //...
   return(0);
}

В листинге 5.3 создаются четыре потока. Два потока, 

ThreadA

 и 

ThreadC

, выпол

няют роль изготовителей, а остальные два (

ThreadB

 и 

ThreadD

) — потребителей. Все

потоки имеют критический раздел, который защищается объектом блокировки чте
ниязаписи 

RWLock

. Потоки 

ThreadB

 и 

ThreadD

 могут входить в свои критические

разделы параллельно или последовательно, но это исключено, если поток 

ThreadA

или 

ThreadC

 пребывает в своем критическом разделе. Потоки 

ThreadA

 и 

ThreadC

 не

могут входить в свои критические разделы параллельно. Частичная таблица решений
для листинга 5.3 показана в табл. 5.6.

Таблица 5.6. Частичная таблица решений для листинга 5.3

Поток A

(выполняет запись)

Поток B

(выполняет чтение)

Поток C

(выполняет запись)

Поток D

(выполняет чтение)

Нет

Нет

Нет

Да

Нет

Нет

Да

Нет

Нет

Да

Нет

Нет

Нет

Да

Нет

Да

Да

Нет

Нет

Нет


background image

5.3. Что такое семафоры

205

5.3.4. Условные переменные

Условная переменная

 представляет собой семафор, используемый для сигнализации

о событии, которое произошло. Сигнала о том, что произошло некоторое событие,
может ожидать один или несколько процессов (или потоков) от других процессов или
потоков. Следует понимать различие между условными переменными и рассмотрен
ными выше мьютексными семафорами. Назначение мьютексного семафора и блоки
ровок чтениязаписи — синхронизировать доступ к данным, в то время как условные
переменные обычно используются для синхронизации последовательности опера
ций. По этому поводу в своей книге 

UNIX Network Programming

 прекрасно высказался

Ричард Стивенс (W. Richard Stevens): “Мьютексы нужно использовать для блокирова
ния, а не для ожидания”.

В листинге 4.6 поток“потребитель” содержал цикл:

15 while(TextFiles.empty())
16 {}

Поток“потребитель” выполнял итерации цикла до тех пор, пока в очереди

TextFiles

 были элементы. Этот цикл можно заменить условной переменной. Поток

“изготовитель” сигналом уведомляет потребителя о том, что в очередь помещены
элементы. Поток“потребитель” может ожидать до тех пор, пока не получит сигнал,
а затем перейдет к обработке очереди.

Условная переменная имеет тип 

pthread_cond_t

. Ниже перечислены типы опе

раций, которые может она выполнять:

• 

инициализация;

• 

разрушение;

• 

ожидание;

• 

ожидание с ограничением по времени;

• 

адресная сигнализация;

• 

всеобщая сигнализация;

Операции инициализации и разрушения выполняются условными переменными

подобно аналогичным операциям других мьютексов. Функции класса

pthread_cond_t

, которые реализуют эти операции, перечислены в табл. 5.7.

Таблица 5.7. Функции класса 

pthread_cond_t

, которые реализуют

операции условных переменных

Операции

Прототипы функций (макросы)

#include <pthread.h>

Инициализация

int pthread_cond_init(
  pthread_cond_t *restrict cond,
  const pthread_condattr_t *restrict attr);

pthread_cond_t cond =
               PTHREAD_COND_INITIALIZER;


background image

206

Глава 5. Синхронизация параллельно выполняемых задач

Окончание табл. 5.7

Операции

Прототипы функций (макросы)

Ожидание

int pthread_cond_wait(
        pthread_cond_t * restrict cond,
        pthread_mutex_t *restrict mutex);

int pthread_cond_timedwait(
  pthread_cond_t * restrict cond,
  pthread_mutex_t *restrict mutex,
  const struct timespec *restrict abstime);

Сигнализация

int pthread_cond_signal(
                    pthread_cond_t *cond);

int pthread_cond_broadcast(
                    pthread_cond_t *cond);

Разрушение

int pthread_cond_destroy(
                    pthread_cond_t *cond);

Условные переменные используются совместно с мьютексами. При попытке за

блокировать мьютекс поток или процесс будет заблокирован до тех пор, пока мью
текс не освободится. После разблокирования поток или процесс получит мьютекс
и продолжит свою работу. При использовании условной переменной ее необходи
мо связать с мьютексом.

//...
pthread_mutex_lock(&Mutex);
pthread_cond_wait(&EventMutex,&Mutex);
//...
pthread_mutex_unlock(&Mutex);

Итак, некоторая задача делает попытку заблокировать мьютекс. Если мьютекс уже

заблокирован, то эта задача блокируется. После разблокирования задача освободит
мьютекс 

Мьютекс

 и при этом будет ожидать сигнала для условной переменной

EventMutex

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

ченно долго. При ожидании с ограничением по времени задача будет ожидать сигнала
в течение заданного интервала времени. Если это время истечет до получения зада
чей сигнала, функция возвратит код ошибки. Затем задача вновь затребует мьютекс.

Выполняя адресную сигнализацию, задача уведомляет другой поток или процесс

о том, что произошло некоторое событие. Если задача ожидает сигнала для заданной
условной переменной, эта задача будет разблокирована и получит мьютекс. Если сразу
несколько задач ожидают сигнала для заданной условной переменной, то разблоки
рована будет только одна из них. Остальные задачи будут ожидать в очереди, и их
разблокирование будет происходить в соответствии с используемой стратегией пла
нирования. При выполнении операции всеобщей сигнализации уведомление получат
все задачи, ожидающие сигнала для заданной условной переменной. При разблокиро
вании нескольких задач они будут состязаться за право владения мьютексом в соот
ветствии с используемой стратегией планирования. В отличие от операции ожида
ния, задача, выполняющая операцию сигнализации, не предъявляет прав на владение
мьютексом, хотя это и следовало бы сделать.

Условная переменная также имеет атрибутный объект, функции которого пере

числены в табл. 5.8.


background image

5.3. Что такое семафоры

207

Таблица 5.8. Функции доступа к атрибутному объекту для условной

переменной типа 

pthread_cond_t

Прототипы функций

Описание

#include <pthread.h>

int pthread_condattr_init(
   pthread_condattr_t * attr);

Инициализирует атрибутный объект условной
переменной, заданный параметром 

attr

, зна

чениями, действующими по умолчанию для
всех атрибутов, определенных реализацией

int pthread_condattr_destroy(
   pthread_condattr_t * attr);

Разрушает атрибутный объект условной пере
менной, заданный параметром 

attr

. Этот объ

ект можно инициализировать повторно, вы
звав функцию 

pthread_condattr_init()

int

pthread_condattr_setpshared(
     pthread_condattr_t *
attr,
     int pshared);

int
  pthread_condattr_getpshared(
    const pthread_condattr_t *
                restrict attr,
    int *restrict pshared);

Устанавливает или возвращает атрибут

process-shared

 атрибутного объекта условной

переменной, заданного параметром 

attr

. Па

раметр 

pshared

 может содержать следующие

значения:

PTHREAD_PROCESS_SHARED

(разрешает блокировку чтениязаписи, разде
ляемую любыми потоками, которые имеют дос
туп к памяти, выделенной для этой условной пе
ременной, даже если потоки принадлежат раз
личным процессам);

PTHREAD_PROCESS_PRIVATE

(условная переменная разделяется между пото
ками одного процесса)

int pthread_condattr_setclock(
   pthread_condattr_t * attr,
   clockid_t clock_id);

int pthread_condattr_getclock(
   const pthread_condattr_t *
               restrict attr,
   clockid_t *
            restrict
clock_id);

Устанавливает или возвращает атрибут 

clock

атрибутного объекта условной переменной, за
данного параметром 

attr

. Атрибут 

clock

представляет собой идентификатор часов, ис
пользуемых для измерения лимита времени
в функции 

pthread_cond_timedwait()

.

По умолчанию для атрибута 

clock

 использует

ся идентификатор системных часов

5.3.4.1. Использование условных переменных для управления

отношениями синхронизации

Условную переменную можно использовать для реализации отношений синхрони

зации, о которых упоминалось выше: стартстарт (СС), финишстарт (ФС), старт
финиш (СФ) и финишфиниш (ФФ). Эти отношения могут существовать между пото
ками одного или различных процессов. В листингах 5.4 и 5.5 представлены примеры
реализации ФС и ФФотношений синхронизации. В каждом примере определено два
мьютекса. Один мьютекс используется для синхронизации доступа к общим данным,
а другой — для синхронизации выполнения кода.