Параллельное и распределенное программирование на С++ (Хьюз, Хьюз) - страница 407

Функцию pthread_cond_signal () небезопасно использовать в обработчике сигналов, который вызывается асинхронно. Даже если это было бы безопасно, имела бы место «гонка» данных между проверками булевой функции pthread_cond_ wait (), которую невозможно эффективно устранить.

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

Логическое обоснование

Несколько запусков по условному сигналу

Для мультипроцессора, скорее всего, невозможно применить функцию pthread_cond_signal(), чтобы избежать разблокировки нескольких потоков, заблокированных с использованием условной переменной. Рассмотрим, например, следующую частичную реализацию функций pthread_cond_wait () и pthread_cond_ signal(), выполняемых потоками в заданном порядке. Один поток пытается «дождаться» нужного значения условной переменной, другой при этом выполняет функцию pthread_cond_signal(), в то время как третий поток уже находится в состоянии ожидания.

>pthread_cond_wait(mutex, cond):

> value = cond->value; /* 1 */

> pthread_mutex_unlock (mutex); /* 2 */

> pthread_mutex_lock (cond->mutex); /* 10 */

> if (value == cond->value) { /* 11 */

> me->next_cond = cond->waiter;

> cond->waiter = me;

> pthread_mutex_unlock(cond->mutex);

> unable_to_run (me);

> } else

> pthread_mutex_unlock (cond->mutex); /* 12 */

> pthread_mutex_lock (mutex); /* 13 * /

>pthread_cond_signal (cond):

> pthread_mutex_lock (cond->mutex); /* 3 */

> cond->value++; /* 4 */

> if (cond->waiter) { /* 5 */

> sleeper = cond->waiter; /* 6 */

> cond->waiter = sleeper->next_cond; /* 7 */

> able_to_run (sleeper); /* 8 */

> }

> pthread_mutex_unlock (cond->mutex); /* 9 */

Итак, в результате одного обращения к функции pthread_cond_signal () сразу несколько потоков могут вернуться из вызова функции pthread_cond_wait () или pthread_cond_timedwait (). Такой эффект называется «фиктивным запуском». Обратите внимание на то, что подобная ситуация является самокорректирующейся благодаря тому, что количество потоков, «пробуждающихся» таким путем, ограничено; например, следующий поток, который вызывает функцию pthread_cond_wait (), после определенной последовательности событий блокируется.

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