errExitEN(s, "pthread_mutex_unlock");

}

Код, показанный выше, работает, но при этом тратит впустую процессорное время, поскольку главный поток постоянно проверяет состояние переменной avail, используя цикл. Эту проблему можно устранить с помощью условной переменной. Это позволит потоку приостанавливать свою работу (ждать), пока другой поток не оповестит его (просигнализирует) о том, что ему нужно что-то сделать (то есть что возникло некое «условие», на которое он должен отреагировать).

Условные переменные всегда используются в сочетании с мьютексами. Мьютекс обеспечивает взаимоисключающий доступ к разделяемому ресурсу, тогда как условная переменная сигнализирует об изменении его состояния (это сигнализирование не имеет ничего общего с сигналами, описанными в главах 20–22; речь здесь скорее идет об оповещении).

30.2.1. Статически выделяемые условные переменные

По аналогии с мьютексами, уловные переменные могут выделяться статически и динамически. Здесь будут рассмотрены статически выделяемые переменные, а динамические мы рассмотрим в подразделе 30.2.5.

Условная переменная имеет тип pthread_cond_t. Как и в случае с мьютексом, перед использованием ее следует инициализировать. В случае со статически выделяемыми условными переменными это делается путем присваивания им значения PTHREAD_COND_INITIALIZER, как показано в следующем примере:

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

Согласно стандарту SUSv3 применение операций, которые будут описаны в оставшейся части этого раздела, к копии условной переменной приводит к неопределенным результатам. Эти операции всегда должны выполняться с оригиналом условной переменной, инициализированным статически, с помощью значения PTHREAD_COND_INITIALIZER, или динамически, путем вызова pthread_cond_init() (описанного в подразделе 30.2.5).

30.2.2. Оповещение и ожидание условных переменных

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

Оповещения передаются с помощью аргумента cond в функциях pthread_cond_signal() и pthread_cond_broadcast(). Функция pthread_cond_wait() блокирует поток, пока условная переменная cond не просигнализирует об изменении.

#include

int pthread_cond_signal(pthread_cond_t *cond);

int pthread_cond_broadcast(pthread_cond_t *cond);

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

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

Разница между функциями pthread_cond_signal() и pthread_cond_broadcast() заключается в том, что происходит, когда в вызове pthread_cond_wait() заблокировано несколько потоков. pthread_cond_signal() просто гарантирует, что по меньшей мере один поток возобновит свою работу; pthread_cond_broadcast() разблокирует все потоки сразу.

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

1. Все ожидающие потоки возобновили работу.

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

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

В отличие от pthread_cond_signal(), функция pthread_cond_broadcast() рассчитана в том числе и на потоки, которые выполняют разные задачи (в этом случае они, скорее всего, имеют разные предикаты, связанные с условной переменной).

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

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