Вызов pthread_cond_signal без блокировки мьютекса

Я где-то читал, что мы должны заблокировать мьютекс перед вызовом pthread_cond_signal и разблокировать мьютекст после его вызова:

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

Мой вопрос: не в порядке ли вызывать методы pthread_cond_signal или pthread_cond_broadcast без блокировки мьютекса?

Ответ 1

Если вы не блокируете мьютекс в кодексе, который изменяет состояние и сигналы, вы можете потерять пробуждения. Рассмотрим эту пару процессов:

Процесс A:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

Процесс B (неверный):

condition = TRUE;
pthread_cond_signal(&cond);

Затем рассмотрим это возможное чередование инструкций, где condition начинается как FALSE:

Process A                             Process B

pthread_mutex_lock(&mutex);
while (condition == FALSE)

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);

condition теперь TRUE, но процесс A застрял в ожидании от переменной условия - он пропустил сигнал пробуждения. Если мы изменим процесс B для блокировки мьютекса:

Процесс B (правильный):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

... то вышеуказанное не может произойти; пробуждение никогда не будет пропущено.

(Обратите внимание, что вы действительно можете переместить pthread_cond_signal() непосредственно после pthread_mutex_unlock(), но это может привести к менее оптимальному планированию потоков, и вы обязательно заблокировали мьютекс уже на этом пути кода из-за изменения условия сам по себе).

Ответ 2

В соответствии с этим руководством:

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

Значение предсказуемого описания поведения планирования объяснялось Дэйвом Бутенхофом (автором Программирование с потоками POSIX) на comp.programming.threads и доступен здесь.

Ответ 3

caf, в вашем примере кода Process B изменяет condition без блокировки мьютекса в первую очередь. Если Process B просто заблокировал мьютекс во время этой модификации, а затем все еще разблокировал мьютекс перед вызовом pthread_cond_signal, не было бы проблем --- я прав об этом?

Я уверен, что позиция в кафе правильная: вызов pthread_cond_signal без владения блокировкой мьютекса - это плохая идея. Но пример кафе фактически не является доказательством в поддержку этой позиции; это просто свидетельствует в пользу гораздо более слабого (практически очевидного) положения о том, что Bad Idea модифицирует разделяемое состояние, защищенное мьютексом, если вы не заблокировали этот мьютекс в первую очередь.

Может ли кто-нибудь предоставить некоторый пример кода, в котором вызов pthread_cond_signal, за которым следует pthread_mutex_unlock, дает правильное поведение, но вызов pthread_mutex_unlock, за которым следует pthread_cond_signal, приводит к некорректному поведению?