Підрозділ 15.6
Клас Mutex
15.6. Клас Mutex Mutex від англ. mutual exclusion — взаємне виключення — примітив синхронізації, що схожий на lock , але має ключову відмінність: Mutex може працювати між процесами , а не лише між потоками одно
15.6. Клас Mutex
Mutex (від англ. mutual exclusion — взаємне виключення) — примітив синхронізації, що схожий на lock, але має ключову відмінність: Mutex може працювати між процесами, а не лише між потоками одного процесу. Це робить його незамінним інструментом у ситуаціях, коли потрібно координувати доступ до ресурсу між кількома запущеними програмами.
Крім міжпроцесного режиму, Mutex підтримує і внутрішньопроцесну синхронізацію. Однак він суттєво повільніший за lock: внутрішньопроцесний lock — це операція на рівні CLR без звернень до ядра ОС; Mutex — це об'єкт операційної системи, і кожне захоплення/звільнення вимагає системного виклику. Тому для синхронізації потоків усередині одного процесу зазвичай краще обирати lock. Mutex обирають тоді, коли міжпроцесна синхронізація є вимогою.
API класу Mutex
| Метод / конструктор | Опис |
|---|---|
new Mutex() |
Локальний (внутрішньопроцесний) mutex, починає у вільному стані |
new Mutex(bool initiallyOwned) |
true — поточний потік одразу стає власником |
new Mutex(bool initiallyOwned, string name) |
Іменований (міжпроцесний) mutex. name — глобальна системна назва |
WaitOne() |
Захоплює mutex. Блокує, якщо зайнятий |
WaitOne(int ms) |
Захоплення з таймаутом. Повертає bool |
ReleaseMutex() |
Звільняє mutex. Лише потік-власник може це зробити |
Dispose() |
Звільняє системний ресурс |
Важлива особливість: mutex є реентерабельним (recursive). Якщо потік, що вже тримає mutex, знову викликає WaitOne(), він пройде без блокування. Але він зобов'язаний викликати ReleaseMutex() рівно стільки разів, скільки викликав WaitOne() — інакше mutex залишиться захопленим.
Базовий приклад: захист спільного ресурсу
Спочатку розглянемо Mutex у ролі звичайного внутрішньопроцесного замку — для розуміння API:
Зверніть на паттерн try/finally: ReleaseMutex() розміщується у блоці finally, щоб гарантувати звільнення навіть при виникненні винятку. Якщо mutex не буде звільнений, всі інші потоки заблокуються назавжди.
Іменований Mutex — захист між процесами
Головна сила Mutex — іменований варіант, що є спільним об'єктом ОС. Два процеси, що створюють Mutex з однаковим іменем, отримують посилання на той самий системний об'єкт:
Параметр out createdNew повертає true, якщо цей процес створив mutex (тобто є першим), або false, якщо mutex вже існував (інший процес вже запущений). Префікс Global\\ означає, що mutex видимий для всіх сесій ОС; Local\\ (або без префікса) — лише для поточної сесії.
Практичний сценарій: клінічна інформаційна система, де лише один запущений екземпляр може здійснювати запис до бази даних. Спроба запустити другий екземпляр завершиться повідомленням, а не пошкодженням даних.
Тайм-аут очікування
Mutex.WaitOne(ms) дозволяє не блокуватись вічно:
AbandonedMutexException
Коли потік завершується, не звільнивши mutex, інші потоки, що чекають, отримають виняток AbandonedMutexException. Це захисний механізм: наступний потік-власник отримає сигнал, що попередній «впав» без коректного завершення, і може прийняти рішення щодо стану спільного ресурсу.
Mutex vs lock: підсумок
| Характеристика | lock |
Mutex |
|---|---|---|
| Область дії | Потоки одного процесу | Потоки та різні процеси |
| Продуктивність | Дуже висока (без ОС-викликів) | Нижча (об'єкт ядра ОС) |
| Реентерабельність | Ні (нова спроба — дедлок) | Так (рекурсивне захоплення) |
| Таймаут очікування | Ні | WaitOne(ms) |
AbandonedMutexException |
Ні | Так |
| Типовий сценарій | Захист даних усередині програми | Singleton-процес, спільний файл |
Правило вибору: для синхронізації потоків усередині одного додатку — lock; якщо потрібна координація між кількома запущеними програмами або захист системного ресурсу — Mutex.