Это атомарная операция «разблокировки и ожидания», которая предотвращает потерянные пробуждения. Потерянное пробуждение происходит следующим образом:
- Мы приобретаем замок, который защищает данные.
- Мы проверяем, нужно ли нам ждать, и видим, что мы делаем.
- Нам нужно снять блокировку, потому что иначе никакой другой поток не сможет получить доступ к данным.
- Мы ждем пробуждения.
Здесь вы можете увидеть риск потерянного пробуждения. Между шагами 3 и 4 другой поток может получить блокировку и отправить пробуждение. Мы сняли блокировку, поэтому другой поток может сделать это, но мы пока не ждем, поэтому мы не получим сигнал.
Пока шаг 2 выполняется под защитой замка, а шаги 3 и 4 являются атомарными, нет риска потерянного пробуждения. Пробуждение нельзя отправить до тех пор, пока данные не будут изменены, что невозможно сделать, пока другой поток не получит блокировку. Поскольку 3 и 4 являются атомарными, любой поток, который видит блокировку как разблокированную, обязательно увидит нас в ожидании.
Это атомарное "разблокирование и ожидание" является основной целью условных переменных и причиной, по которой они всегда должны быть связаны с мьютексом и предикатом.
В приведенном выше коде потребитель не ждет первых нескольких уведомлений, потому что он спит. Не пропущено ли уведомление в этом случае? Разве этот случай не похож на состояние гонки между # 3 и # 4?
Неа. Не может быть.
Либо не ожидающий потребитель держит блокировку, либо нет. Если потребитель, который не ждет, держит замок, он не может ничего пропустить. Предикат не может измениться, когда он удерживает блокировку.
Если потребитель не держит замок, то не имеет значения, что он пропускает. Когда он проверяет, нужно ли заблокировать на шаге 2, если он что-то пропустил, он обязательно увидит его на шаге 2 и увидит, что ему не нужно ждать, поэтому он не будет ждать пропущенного пробуждения.
Так что, если предикат таков, что поток не должен ждать, поток не будет ждать, потому что он проверяет предикат. Нет возможности пропустить пробуждение до шага 1.
Единственный раз, когда требуется фактическое пробуждение, это когда поток переходит в спящий режим. Атомная разблокировка и спящий режим гарантируют, что поток может принять решение о переходе в спящий режим только в том случае, если он удерживает блокировку и пока то, чего ему нужно ждать, еще не произошло.