Как несколько потоков (ожидающих переменную условия) могут получить соответствующую блокировку при вызове condition_all ()? - PullRequest
4 голосов
/ 12 января 2020

Из теории ожидающий поток (скажем, Thread_1) сначала получает мьютекс, а затем ожидает переменную условия, вызывая wait (). Вызов wait () немедленно разблокирует мьютекс. Когда другой поток (скажем, Thread_2) вызывает notify (), ожидающий поток (Thread_1) пробуждается, и тот же мьютекс блокируется до возврата вызова wait (..).

Теперь скажем, несколько потоков ожидают переменную условия в данный момент времени (скажем, Thread_1, Thread_2 и Thread_3). Теперь другой поток (Thread_4) вызывает notify_all (), который уведомит все 3 потока, ожидающих условную переменную. Когда они проснутся, как все 3 смогут заблокировать мьютекс, что должно произойти до того, как вызов wait (...) вернется? Только один поток (из 3 ожидающих потоков) может получить мьютекс. Тогда какова цель notify_all (), если он может разблокировать только один поток? В чем разница в результатах (с точки зрения ожидающих потоков) между notify () и notify_all ()?

1 Ответ

2 голосов
/ 12 января 2020

Как все три из них могут блокировать мьютекс?

Они блокируют его по одному, как всегда.

Функция 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() звонка ...

... и так далее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...