Условная переменная не содержит никакой информации о состоянии. Это всего лишь механизм обмена данными о состоянии приложения. Если в момент создания уведомления условную переменную не ожидает ни один поток, это уведомление теряется. Если позже какой-то поток начнет ждать условную переменную, он будет разблокирован только после создания нового уведомления.
Функция pthread_cond_timedwait() делает то же самое, что и pthread_cond_wait(), однако ее аргумент abstime позволяет ограничить время ожидания потоком уведомления от условной переменной.
#include
int pthread_cond_timedwait(pthread_cond_t *
const struct timespec *
Возвращает 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(), не будет заблокирован через условную переменную, ни один другой поток не сможет получить мьютекс и послать уведомление.