next up previous contents
Next: Сокеты Up: Переменные состояния Previous: Инициализация переменной состояния   Contents

Блокировка через переменную состояния

Функция pthread_cond_wait() используется, чтобы атомарно освободить мьютекс и заставить вызывающий поток блокироваться по переменной состояния. Функция pthread_cond_wait() возвращает 0 - после успешного завершения. Любое другое значение указывает, что произошла ошибка. Пример использования функции:

#include <pthread.h> 

pthread_cond_t cv; 

pthread_mutex_t mutex; 

int ret; 

ret = pthread_cond_wait(&cv, &mutex);

Блокированный поток пробуждается с помощью вызовов
pthread_cond_signal(), pthread_cond_broadcast(), или может быть прерван соответствующим сигналом. Любое изменение состояния, связанного с его переменной, не может быть вызвано возвратом из pthread_cond_wait(), и любое такое состояние должно быть перепроверено. Процедура pthread_cond_wait() всегда возвращает запертый мьютекс, который принадлежит вызывающему потоку, даже если возникла ошибка. Эта функция блокируется, пока не придет сообщение о нужном состоянии. Она атомарно освобождает связанный с ней закрытый мьютекс перед блокированием, и атомарно захватывает его снова, перед возвратом.

Проверка состояния обычно проводится в цикле while, который вызывает pthread_cond_wait():

pthread_mutex_lock();

while(condition_is_false) 

  pthread_cond_wait(); 

pthread_mutex_unlock();

Чтобы разблокировать определенный поток, используется функция
pthread_cond_signal():

int pthread_cond_signal(pthread_cond_t *cv);
Она разблокирует поток, заблокированный переменной состояния cv. Функция pthread_cond_signal() возвращает 0 - после успешного завершения. Любое другое значение указывает, что произошла ошибка.

Следует всегда вызывать pthread_cond_signal() под защитой мьютекса, используемого с сигнальной переменной состояния. В ином случае переменная состояния может измениться между тестированием соответствующего состояния и блокировкой в вызове
pthread_cond_wait(), что может вызвать бесконечное ожидание. Если никакие потоки не блокированы по переменной состояния, вызов pthread_cond_signal () не будет иметь никакого эффекта.

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

pthread_mutex_t count_lock; 

pthread_cond_t count_nonzero; 

unsigned count; 

decrement_count() {

  pthread_mutex_lock(&count_lock); 

  while (count == 0) 

    pthread_cond_wait(&count_nonzero, &count_lock); 

  count = count - 1; 

  pthread_mutex_unlock(&count_lock); 

}

 

increment_count() {

  pthread_mutex_lock(&count_lock); 

  if (count == 0)

    pthread_cond_signal(&count_nonzero); 

  count = count + 1;

  pthread_mutex_unlock(&count_lock); 

}

Можно также блокировать поток до наступления определенного события. Для этих целей используется функция
pthread_cond_timedwait():

int pthread_cond_timedwait(pthread_cond_t *cv,

    pthread_mutex_t *mp,

    const struct timespec *abstime);

pthread_cond_timedwait() блокирует поток до сообщения о наступлении состояния или до наступления момента времени, указанного abstime; pthread_cond_timedwait() всегда возвращает мьютекс, запертый и принадлежащий вызывающему потоку, даже если происходит ошибка. После успешного завершения
pthread_cond_timedwait() возвращает 0. В случае ошибки возвращается отличное от 0 значение. Пример вызова функции:

#include <pthread.h> 

#include <time.h> 

pthread_timestruc_t to;

pthread_cond_t cv; 

pthread_mutex_t mp;

timestruct_t abstime; 

int ret; 

/* ожидание переменной состояния */ 

ret = pthread_cond_timedwait(&cv, &mp, &abstime);

pthread_mutex_lock(&m); 

to.tv_sec = time(NULL) + TIMEOUT; 

to.tv_nsec = 0; 

while (cond == FALSE) {

  err = pthread_cond_timedwait(&c, &m, &to); 

  if (err == ETIMEDOUT) {

     /* таймаут */ 

     break;

  } 

pthread_mutex_unlock(&m);

Все блокированные потоки можно разблокировать функцией
pthread_cond_broadcast():

int pthread_cond_broadcast(pthread_cond_t *cv);
pthread_cond_broadcast() разблокирует все потоки, блокированные переменной состояния, на которую указывает cv, определенная
pthread_cond_wait(). Если ни один поток не блокирован этой переменной состояния, вызов pthread_cond_broadcast() не будет иметь никакого эффекта.

pthread_cond_broadcast() возвращает 0 - после успешного завершения - или любое другое значение в случае ошибки.

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

#include <pthread.h>

pthread_mutex_t rsrc_lock; 

pthread_cond_t rsrc_add;

unsigned int resources; 

get_resources(int amount) {

  pthread_mutex_lock(&rsrc_lock);

  while (resources < amount) 

    pthread_cond_wait(&rsrc_add, &rsrc_lock); 

  resources -= amount; 

  pthread_mutex_unlock(&rsrc_lock); 

}

 

add_resources(int amount) {

  pthread_mutex_lock(&rsrc_lock); 

  resources += amount;

  pthread_cond_broadcast(&rsrc_add); 

  pthread_mutex_unlock(&rsrc_lock); 

}

Функция pthread_cond_destroy() используется для удаления состояния, ассоциированного с переменной состояния:

#include <pthread.h> 

pthread_cond_t cv; 

int ret; 

/* Переменная состояния удалена */ 

ret = pthread_cond_destroy(&cv);

pthread_cond_destroy() возвращает 0 - после успешного завершения - или любое другое значение в случае ошибки.


next up previous contents
Next: Сокеты Up: Переменные состояния Previous: Инициализация переменной состояния   Contents
2004-06-22