Как все три из них могут блокировать мьютекс?
Они блокируют его по одному, как всегда.
Функция wait(...)
может быть реализован так:
def wait(cond_var, mutex):
tricky_internal_wait(cond_var, mutex)
lock(mutex)
Функция tricky_internal_wait(c,m)
будет атомарно разблокировать мьютекс и заблокировать вызывающий поток в очереди, связанной с cond_var
, но нет причины, по которой * Вызов 1015 * в конце должен отличаться от обычного lock(mutex)
.
Когда в приведенном выше примере будет уведомлено cond_var
, поток проснется, а затем вызовет lock(mutex)
, а затем, если какой-то другой поток уже заблокировал мьютекс, ОС заблокирует вызывающего в очереди, связанной с mutex
. Вызывающая сторона не может вернуться из вызова wait()
, пока не вернется из вызова lock()
, и не может вернуться из lock()
, пока мьютекс не станет доступным, и вызывающий абонент не получит его. Точно так же, как lock()
всегда работает.
A практическая реализация wait(c,m)
, вероятно, сделает вещи более эффективно: вероятно, он переместит поток непосредственно из очереди cond_var
в mutex
очередь, даже не пробуждая поток, если mutex
уже использовался другим потоком.
Тогда какова цель notify_all (), если он может разблокировать только один поток?
Это не разблокирует только один. Он разблокирует их все, ...
... По одному.
Предположим, что некоторый поток T вызывает notify_all(cond_var)
, тогда как потоки X, Y и Z все ожидают условия в foobar()
:
def foobar():
lock(mutex)
while condition_is_not_satisfied():
wait(cond_var, mutex)
do_some_thing_that_requires_condition_to_be_satisfied()
unlock(mutex)
Возможно, поток Z будет первым, кто вернется после вызова wait()
. Он снова проверит, чтобы убедиться, что условие действительно выполнено, и затем он выполнит свою задачу, а затем разблокирует mutex
и вернется из foobar()
.
Пока поток Z не разблокирует мьютекс и возвращает, потоки X и Y не смогут вернуться из вызова wait()
.
Может быть, после того, как Z разблокирует мьютекс, следующий, который вернется из wait (), будет X. X затем проверит посмотреть, выполнено ли условие. Возможно, действие Z означает, что условие не больше не выполняется. В этом случае X снова wait()
и вызов wait()
освободит мьютекс. Или, может быть, условие все еще удовлетворено, и X сделает это, и явно разблокирует мьютекс и вернется из foobar()
.
В любом случае поток X освободит мьютекс, а затем поток Y будет возможность вернуться с wait()
звонка ...
... и так далее.