Читать «UNIX: взаимодействие процессов» онлайн - страница 117
Уильям Ричард Стивенс
Потребитель ждет, пока значение nready.nready не станет отличным от нуля
72-76 Потребитель просто ждет, пока значение счетчика nready. nready не станет отличным от нуля. Поскольку этот счетчик используется совместно с производителями, его значение можно проверять только при блокировке соответствующего взаимного исключения. Если при проверке значение оказывается нулевым, мы вызываем pthread_cond_wait для приостановки процесса. При этом выполняются два атомарных действия:
1. Разблокируется nready.mutex.
2. Выполнение потока приостанавливается, пока какой-нибудь другой поток не вызовет pthread_cond_signal для этой условной переменной.
Перед возвращением управления потоку функция pthread_cond_wait блокирует nready.mutex. Таким образом, если после возвращения из функции мы обнаруживаем, что счетчик имеет ненулевое значение, мы уменьшаем этот счетчик (зная, что взаимное исключение заблокировано) и разблокируем взаимное исключение. Обратите внимание, что после возвращения из pthread_cond_wait мы всегда заново проверяем условие, поскольку может произойти ложное пробуждение при отсутствии выполнения условия. Различные реализации стремятся уменьшить количество ложных пробуждений, но они все равно происходят.
Код, передающий сигнал условной переменной, выглядит следующим образом:
struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
переменные, для которых устанавливается условие
} var = { PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, … };
Pthread_mutex_lock(&var.mutex);
установка истинного значения условия
Pthread_cond_signal(&var.cond);
Pthread_mutex_unlock(&var.mutex);
В нашем примере переменная, для которой устанавливалось условие, представляла собой целочисленный счетчик, а установка условия означала просто увеличение счетчика. Мы оптимизировали программу, посылая сигнал только при изменении значения счетчика с 0 на 1.
Код, проверяющий условие и приостанавливающий процесс, если оно не выполняется, обычно выглядит следующим образом:
Pthread_mutex_lock(&var.mutex);
while (условие ложно)
Pthread_cond_wait(&var.cond, &var.mutex);
изменение условия
Pthread_mutex_unlock(&var.mutex);
Исключение конфликтов блокировок
В приведенном выше фрагменте кода, как и в листинге 7.6, функция pthread_cond_signal вызывалась потоком, блокировавшим взаимное исключение, относящееся к условной переменной, для которой отправлялся сигнал. Мы можем представить себе, что в худшем варианте система немедленно передаст управление потоку, которому направляется сигнал, и он начнет выполняться и немедленно остановится, поскольку не сможет заблокировать взаимное исключение. Альтернативный код, помогающий этого избежать, для листинга 7.6 будет иметь следующий вид: