Давайте временно представим, что mutex
не заблокирован при возврате из wait
:
Поток 1:
Блокирует mutex
, проверяет предикат ( что бы это ни было), и, обнаружив предикат в неприемлемой форме, ждет, пока какой-нибудь другой поток вернет его в приемлемую форму. Ожидание атомарно переводит поток 1 в спящий режим и разблокирует mutex
. При разблокированном mutex
некоторый другой поток получит разрешение перевести предикат в допустимое состояние (предикат не является естественно безопасным для потока).
Поток 2:
Одновременно этот поток пытается заблокировать mutex
и поместить предикат в состояние, приемлемое для потока 1, чтобы продолжить его ожидание. Он должен сделать это с заблокированным mutex
. mutex
защищает предикат от доступа (либо чтения, либо записи) более чем к одному потоку за раз.
Как только поток 2 переводит mutex
в приемлемое состояние, он уведомляет condition_variable
и разблокирует mutex
(порядок этих двух действий не имеет отношения к этому аргументу).
Thread 1:
Теперь поток 1 был уведомлен, и мы предполагаем гипотетическое, что mutex
не блокируется при возврате с wait
. Первое, что нужно сделать потоку 1, это проверить предикат, чтобы увидеть, является ли он действительно приемлемым (это может быть ложным пробуждением). Но он не должен проверять предикат без блокировки mutex
. В противном случае другой поток может изменить предикат сразу после того, как этот поток проверит его, аннулировав результат этой проверки.
Так что очень первое, что этот поток должен сделать при пробуждении, это заблокировать mutex
и , затем проверьте предикат.
Так что действительно удобнее то, что mutex
блокируется по возвращении из wait
. В противном случае ожидающий поток должен был бы вручную заблокировать его 100% времени.
Давайте еще раз посмотрим на события, когда поток 1 входит в wait
: я сказал, что происходит сон и разблокировка атомарно . Это очень важно. Представьте, что поток 1 должен вручную разблокировать mutex
и , а затем вызвать wait
: в этом гипотетическом сценарии поток 1 может разблокировать mutex
, а затем прерваться, пока другой поток получит mutex
, изменяет предикат, разблокирует mutex
и сигнализирует condition_variable
, все до того, как поток 1 вызовет wait
. Теперь поток 1 спит вечно, потому что ни один поток не увидит, что предикат нуждается в изменении, а condition_variable
нужна сигнализация.
Так что обязательно , что unlock
/ enter - wait
случается атомно . И это облегчает использование API, если lock
/ exit- wait
также происходит атомарно.