Підрозділ 15.5
Клас AutoResetEvent
15.5. Клас AutoResetEvent Monitor.Wait і Monitor.Pulse — потужні інструменти координації, але вони вимагають, щоб і виробник, і споживач використовували один і той самий об'єкт замку . У складніших сценаріях, д
15.5. Клас AutoResetEvent
Monitor.Wait і Monitor.Pulse — потужні інструменти координації, але вони вимагають, щоб і виробник, і споживач використовували один і той самий об'єкт замку. У складніших сценаріях, де потоки координуються через незалежні сигнали, більш природним є клас AutoResetEvent.
AutoResetEvent — це примітив синхронізації, що моделює автоматичне скидання сигнального прапорця. Він має два стани:
- Сигнальний (signaled, «зелене світло»): потоки, що чекають через
WaitOne(), можуть продовжити виконання - Несигнальний (non-signaled, «червоне світло»): потоки блокуються на
WaitOne()
Ключова особливість, яка відрізняє AutoResetEvent від ManualResetEvent: після того як один очікуючий потік пройшов через WaitOne(), прапорець автоматично скидається у несигнальний стан. Наступний потік знову буде заблокований — доки не надійде новий сигнал. Це нагадує турнікет: пропускає одного і автоматично закривається.
API класу AutoResetEvent
| Метод / конструктор | Опис |
|---|---|
new AutoResetEvent(bool initialState) |
Створює об'єкт. true — початково сигнальний, false — несигнальний |
Set() |
Переводить у сигнальний стан. Один очікуючий потік пройде через WaitOne(), після чого стан знову стане несигнальним |
Reset() |
Примусово переводить у несигнальний стан |
WaitOne() |
Блокує поточний потік до отримання сигналу |
WaitOne(int ms) |
Блокує з таймаутом. Повертає true, якщо сигнал надійшов, false — якщо час вичерпано |
Клінічний приклад: відправка результатів аналізу
Лаборант завершує аналіз і сигналізує лікарю, що результати готові. Лікар чекає сигналу, перш ніж відкрити результати:
Після виклику resultsReady.Set() лікар отримує сигнал і прокидається. Прапорець автоматично скидається у несигнальний стан — якщо б з'явився ще один лікар, що чекає, він знову заблокувався б до наступного Set().
Координація кількох етапів: конвеєр обробки
AutoResetEvent особливо зручний для організації послідовних етапів конвеєра, де кожен наступний крок починається тільки після завершення попереднього. У клінічній системі: реєстрація → огляд → аналізи → призначення — кожен крок сигналізує готовність наступному:
Усі чотири потоки запущені одночасно, але виконуються суворо по черзі: кожен чекає сигналу від попереднього через відповідний AutoResetEvent. Це ефективніше за послідовний запуск потоків: поки реєстратура реєструє, лікар і лабораторія вже «готові» і не витрачають час на ініціалізацію.
WaitOne з таймаутом
Якщо потік не може чекати нескінченно — наприклад, після 5 секунд очікування слід надіслати сповіщення адміністратору — використовується WaitOne(ms):
WaitOne(ms) дає потоку можливість «зупинитись і перевірити» — реалізація тайм-аутів без зайвого ускладнення коду.
AutoResetEvent vs ManualResetEvent
AutoResetEvent скидає прапорець автоматично після пробудження одного потоку. Якщо ж потрібно одночасно «відкрити ворота» для всіх очікуючих потоків і тримати їх відкритими до явного скидання, використовується ManualResetEvent:
| Характеристика | AutoResetEvent | ManualResetEvent |
|---|---|---|
Скидання після WaitOne |
Автоматично | Вручну через Reset() |
Пропускає через Set() |
Один потік | Всі очікуючі |
| Аналогія | Турнікет | Відкриті ворота |
| Типовий сценарій | Передача одного завдання конкретному виконавцю | Старт усіх потоків одночасно |
У медичному контексті: AutoResetEvent — це черговий реєстратор, що передає один квиток одному пацієнту; ManualResetEvent — це оголошення по радіо, що відкривається кабінет і туди одночасно йдуть усі пацієнти, яким призначено.