Статус Mutex после ложного пробуждения

Рассмотрим эту базовую многопоточную программу, используя pthreads. У нас есть основной stream, создающий еще один stream, который выполняет некоторую работу.

bool done = false; mutex m; condition c; void foo() { pthread_mutex_lock(&m); //while(!done) { pthread_cond_wait(&c, &m); // Spuriously wakeup while child is doing work. // child thread has NOT unlocked the mutex yet // Do I now own the mutex? // or am I waiting for child to unlock it? //} pthread_mutex_unlock(&m); } void * child(void *arg) { pthread_mutex_lock(&m); some_intense_work(); // this work is done while mutex is held! // the main thread spuriously wakes up // while this work is being done // (while this child thread is holding the mutex) done = true; pthread_cond_broadcast(&c); pthread_mutex_unlock(&m); } int main(int argc, char *argv[]) { pthread_t p; pthread_create(&p, NULL, child, NULL); foo(); } 

Представьте, что мы реализуем ожидание без окружения while-clause для предиката, хотя мы знаем, что никто никогда не должен этого делать.

Теперь, если, пока дочерний stream выполняет свою работу, в основном streamе возникает побочная пробуждение, каков будет статус мьютекса m? Будет ли основной stream владеть им без того, чтобы ребенок сначала отпирал его, чтобы оба его обладали?

Или ложное пробуждение пропускает только ожидание условия, но не ждет освобождения мьютекса?

Вызов pthread_cond_wait() не может «ложно» просыпаться, в то время как в каком-то другом streamе сохраняется связанный мьютекс. Когда pthread_cond_wait() возвращается успешно, он будет требовать мьютекс, поэтому он не сможет успешно вернуться, пока мьютекс не будет доступен.

В вашем примере может возникнуть ложное следование, потому что foo() может вызывать pthread_cond_wait() и иметь побочный след, прежде чем child() когда-либо получит возможность вызвать pthread_mutex_lock() в первую очередь.

Другая проблема в вашем примере (с отключенным комментарием) – это то, что вызов pthread_cond_wait() возможен, чтобы никогда не проснуться. Этот сценарий может произойти, если child() завершает всю обработку, прежде чем foo() справится с получением мьютекса. в этом случае child() вызывается pthread_cond_broadcast() до того, как основной stream ждет в pthread_cond_wait() поэтому основной stream будет пропускать трансляцию. Поскольку foo() никогда не done проверку при удержании мьютекса, он не замечает, что child() завершил свою работу.

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