Если вы не заблокируете мьютекс в пути кода, который изменяет состояние и сигналы, вы можете потерять пробуждение. Рассмотрим эту пару процессов:
Процесс А:
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
, но процесс А застревают ожидания на переменном состоянии - это пропущенный сигнал пробуждения. Если мы изменим процесс B, чтобы заблокировать мьютекс:
Процесс B (правильный):
pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
... тогда вышеуказанное не может произойти; пробуждение никогда не будет пропущено.
(Обратите внимание, что вы можете переместить сам pthread_cond_signal()
объект после pthread_mutex_unlock()
, но это может привести к менее оптимальному планированию потоков, и вы обязательно заблокировали мьютекс уже в этом пути кода из-за изменения самого условия).
pthread_signal_cond()
можно переместить после разблокировки мьютекса, хотя, вероятно, лучше этого не делать. Возможно, правильнее будет сказать, что в момент, когда вы вызываетеpthread_signal_cond()
, вам уже нужно было заблокировать мьютекс, чтобы изменить само условие.pthread_cond_timedwait()
илиpthread_cond_wait()
операции, между этим мьютексом и переменной условия формируется динамическая привязка, которая остается в силе до тех пор, пока в по крайней мере один поток заблокирован для переменной условия. В течение этого времени эффект от попытки любого потока ожидать для этой переменной условия с использованием другого мьютекса не определен. "Согласно этому руководству:
Значение оператора предсказуемого расписания объяснил Дэйв Бутенхоф (автор книги « Программирование с помощью потоков POSIX» ) на comp.programming.threads и доступен здесь .
источник
caf, в вашем примере кода процесс B модифицируется
condition
без предварительной блокировки мьютекса. Если бы процесс B просто заблокировал мьютекс во время этой модификации, а затем все еще разблокировал мьютекс перед вызовомpthread_cond_signal
, не было бы проблем - я прав в этом?Я интуитивно считаю, что позиция caf верна: вызов
pthread_cond_signal
без блокировки мьютекса - плохая идея. Но пример caf на самом деле не является доказательством в поддержку этой позиции; это просто свидетельство в поддержку гораздо более слабой (практически самоочевидной) позиции о том, что изменение общего состояния, защищенного мьютексом, является плохой идеей, если вы сначала не заблокировали этот мьютекс.Может ли кто-нибудь предоставить образец кода, в котором вызов,
pthread_cond_signal
за которым следует,pthread_mutex_unlock
дает правильное поведение, но вызов,pthread_mutex_unlock
за которым следует,pthread_cond_signal
дает неправильное поведение?источник
pthread_cond_signal
afterpthread_mutex_unlock
может привести к потере пробуждения, потому что сигнал перехватывается «неправильным» потоком, который заблокирован после просмотра изменения в предикате. Это проблема, только если одна и та же переменная условия может использоваться более чем для одного предиката, а вы не используетеpthread_cond_broadcast
, что в любом случае является редкой и хрупкой схемой.