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

Функция pthread_cond_timedwait() делает то же самое, что и pthread_cond_wait(), однако ее аргумент abstime позволяет ограничить время ожидания потоком уведомления от условной переменной.

#include

int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,

const struct timespec *abstime);

Возвращает 0 при успешном завершении или положительное число, если произошла ошибка

Аргумент timespec представляет собой структуру timespec (см. подраздел 23.4.2), которая обозначает абсолютное время, выраженное в секундах и наносекундах, прошедших с момента начала эры UNIX (см. раздел 10.1). Если временной интервал, указанный в timespec, истекает до отправки уведомления условной переменной, функция pthread_cond_timedwait() возвращает ошибку ETIMEDOUT.

Применение условной переменной в примере с производителем-потребителем

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

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

static int avail = 0;

Участки кода, представленные в данном разделе, можно найти в файле threads/prod_condvar.c, который входит в состав архива с исходным кодом, прилагающимся к этой книге.

Код потоков, генерирующих элементы, остается почти без изменений, если не считать появления вызова pthread_cond_signal():

s = pthread_mutex_lock(&mtx);

if (s!= 0)

errExitEN(s, "pthread_mutex_lock");

avail++; /* Уведомляем потребителя о готовности еще одного элемента */

s = pthread_mutex_unlock(&mtx);

if (s!= 0)

errExitEN(s, "pthread_mutex_unlock");

s = pthread_cond_signal(&cond); /* Возобновляем работу заблокированного потребителя */

if (s!= 0)

errExitEN(s, "pthread_cond_signal");

Прежде чем приступать к рассмотрению кода потребителя, следует подробней остановиться на функции pthread_cond_wait(). Ранее уже отмечалось, что у условной переменной всегда есть связанный с ней мьютекс. Оба эти объекта передаются качестве аргументов в функцию pthread_cond_wait(), которая выполняет следующие действия:

• открывает мьютекс, указанный в аргументе mutex;

• блокирует вызывающий поток до тех пор, пока другой поток не уведомит условную переменную cond;

• заново закрывает mutex.

Для выполнения этих шагов была создана функция pthread_cond_wait(), потому что обычно для доступа к разделяемой переменной приходится писать следующий код:

s = pthread_mutex_lock(&mtx);

if (s!= 0)

errExitEN(s, "pthread_mutex_lock");

while (/* Проверяем, не находится ли разделяемая переменная в нужном нам состоянии */)

pthread_cond_wait(&cond, &mtx);

/* Теперь разделяемая переменная в нужном состоянии; выполняем какую-то работу */

s = pthread_mutex_unlock(&mtx);

if (s!= 0)

errExitEN(s, "pthread_mutex_unlock");

Причину размещения вызова pthread_cond_wait() внутри цикла while, а не в инструкции if вы узнаете в следующем разделе.

В приведенном выше коде обе попытки доступа к разделяемой переменной должны быть защищены мьютексом (почему — было рассказано выше). Иными словами, мьютекс и условная переменная имеют естественную связь.

1. Поток закрывает мьютекс в ходе подготовки к проверке состояния разделяемой переменной.

2. Состояние разделяемой переменной проверено.

3. Если разделяемая переменная находится не в том состоянии, которое нам нужно, поток должен открыть мьютекс (чтобы другие потоки могли получить доступ к разделяемой переменной) до начала ожидания условной переменной.

4. Когда поток снова возобновляет работу в результате получения уведомления от условной переменной, мьютекс опять должен быть закрыт, потому что следующим шагом поток, как правило, пытается получить доступ к разделяемой переменной.

Функция pthread_cond_wait() автоматически выполняет операции открытия и закрытия мьютекса, необходимые в последних двух пунктах. В третьем пункте открытие мьютекса и блокирование посредством условной переменной происходит автоматически. Иными словами, пока поток, вызывающий pthread_cond_wait(), не будет заблокирован через условную переменную, ни один другой поток не сможет получить мьютекс и послать уведомление.

Перейти на страницу:

Похожие книги