■ Функция thread_function()
■ Функция set_thread_flag()
thread_function()
была заблокирована в ожидании сигнала, она разблокируется и снова проверяет флаг.Но существует одна проблема: возникает гонка между операцией проверки флага и операцией сигнализирования или ожидания сигнала. Предположим, что функция thread_function()
set_thread_flag()
. Она устанавливает флаг и сигнализирует об изменении условной переменной. Но поскольку в данный момент нет потока, ожидающего получения этого сигнала (вспомните, что функция thread_function()
была прервана перед тем, как перейти в режим ожидания), сигнал окажется потерян. Когда Linux вновь активизирует дочерний поток, он начнет ждать сигнал, который, возможно, никогда больше не придет.Чтобы избежать этой проблемы, необходимо одновременно захватить и флаг, и сигнальную переменную с помощью исключающего семафора. К счастью, в Linux это предусмотрено. Любая сигнальная переменная должна использоваться совместно с исключающим семафором для предотвращения состояния гонки. Наша потоковая функция должна следовать такому алгоритму:
■ В цикле необходимо захватить исключающий семафор и прочитать значение флага.
■ Если флаг установлен, нужно разблокировать семафор и выполнить требуемые действия.
■ Если флаг не установлен, одновременно выполняются операции освобождения семафора и перехода в режим ожидания сигнала.
Вся суть заключена в третьем этапе, на котором Linux позволяет выполнить атомарную операцию освобождения исключающего семафора и перехода в режим ожидания сигнала. Вмешательство других потоков при этом не допускается.
Сигнальная переменная имеет тип pthread_cond_t
■ Функция pthread_cond_init()
pthread_cond_t
. Второй аргумент (указатель на объект атрибутов сигнальной переменной) игнорируется в Linux. Исключающий семафор должен инициализироваться отдельно, как описывалось в разделе 4.4.2, "Исключающие семафоры".■ Функция pthread_cond_signal()
pthread_cond_t
.Похожая функция pthread_cond_broadcast()
■ Функция pthread_cond_wait()
pthread_cond_t
. Второй аргумент — это указатель на объект исключающего семафора (тип pthread_mutex_t
).В момент вызова функции pthread_cond_wait()
Перечисленные ниже этапы должны выполняться всякий раз, когда программа тем или иным способом меняет результат проверки условия, контролируемого сигнальной переменной (в нашей программе условие — это значение флага):
1. Захватить исключающий семафор, дополняющий сигнальную переменную.
2. Выполнить действие, включающее изменение результата проверки условия (в нашем случае — установить флаг).
3. Послать сигнал (возможно, широковещательный) об изменении условия.
4. Освободить исключающий семафор.
В листинге 4.14 показана измененная версия предыдущего примера, в которой на этот раз флаг защищается сигнальной переменной. Обратите внимание на то, что в функции thread_function()
thread_flag
. Захват автоматически снимается функцией pthread_cond_wait()
перед тем, как поток оказывается заблокированным, и также автоматически восстанавливается по завершении функции:#include
int thread_flag;
pthread_cond_t thread_flag_cv;
pthread_mutex_t thread_flag_mutex;