В то время как блокировки предоставляют способ сохранить нити из пути друг друга, они действительно не обеспечивают, механизм для них, чтобы сотрудничать (синхронизируют)

. Я быстро покрою принципы для синхронизации нитей, не вводя гонки. Как будете видеть, они очень не отличаются от принципов для надлежащего блокирования.
В.NET Структуре функциональность синхронизации внедрена в классах ManualResetEvent и AutoResetEvent. Эти классы предоставляют Набор, Сброс и методы WaitOne. Метод WaitOne заставляет нить блокировать, пока событие находится в государстве сброса. Когда другая нить назовет метод Набора, AutoResetEvents позволит одну нить, которую названный WaitOne, чтобы открыть, в то время как ManualResetEvents откроет все нити ожидания.
Обычно события используются, чтобы сигнализировать, что более сложная собственность программы держится. Например, у программы могла бы быть очередь работы для нити, и событие используется, чтобы сигнализировать к нити, что очередь не пуста. Обратите внимание на то, что это вводит инвариант программы, что событие должно быть установлено, если и только если очередь не пуста. Правила для надлежащего блокирования требуют, чтобы, если код требует инварианта, были блокировки, которые предоставляют исключительный доступ для всей памяти, связанной с тем инвариантом. Применение этого принципа в очереди предлагает, чтобы весь доступ к событию и очереди произошел только после ввода общей блокировки.
К сожалению, этот дизайн может вызвать тупик. Возьмите этот сценарий, например. Нить A вводит блокировку и должна ждать очереди, чтобы быть заполненной (владея блокировкой очереди). Пронизывайте B, который пытается добавить вход в очередь, которую Нить потребности, попытается ввести блокировку очереди прежде, чем изменить очередь и таким образом заблокировать на Нити A. Зайдите в тупик!
В целом это - плохая идея держать блокировки, ожидая на событиях. В конце концов, почему локаут все другие нити от структуры данных, когда нить ждет на чем-то еще? Просто просите тупики. Установившаяся практика должна выпустить блокировку и затем ждать на событии. Однако, для события и очереди теперь возможно испытывать недостаток синхронизации. Мы сломали инвариант, что событием является точный индикатор того, когда очередь не пуста. Типичное решение состоит в том, чтобы ослабить инвариант в этом случае к, "если событие перезагружено, то очередь пуста". Этот инвариант достаточно силен, которым все еще безопасно ждать на событии, не рискуя ждущий навсегда. Этот расслабленный инвариант означает, что, когда нить возвращается из WaitOne, это не может предположить, что у очереди есть элемент в ней. Бодрствующая нить должна ввести блокировку очереди и проверить, что у очереди есть элемент. Если бы не (говорят, некоторая другая нить удалила вход), то это должно ждать снова. Если справедливость среди нитей важна, у этого решения есть проблема, но это выполняет работу хорошо в большинстве целей.