Ответ
Monitor, Mutex и Semaphore — это примитивы синхронизации в .NET для управления доступом к общим ресурсам в многопоточной среде. Их ключевые различия — в области действия (процесс/система) и количестве потоков, которым разрешен одновременный доступ.
1. Monitor (Внутрипроцессный, эксклюзивный доступ)
- Область действия: Только в пределах одного процесса (AppDomain).
- Доступ: Позволяет войти в критическую секцию только одному потоку.
- Использование: Базовый механизм для
lock. Легковесный.
private object _lockObj = new object();
void AccessResource()
{
lock (_lockObj) // Использует Monitor внутри
{
// Только один поток здесь
}
}
2. Mutex (Межпроцессный, эксклюзивный доступ)
- Область действия: Может быть системным (именованным), работая между разными процессами.
- Доступ: Позволяет войти в критическую секцию только одному потоку (или процессу). Имеет понятие "владельца".
- Использование: Для синхронизации между процессами (например, гарантия, что только один экземпляр приложения запущен).
// Именованный мьютекс для межпроцессной синхронизации
using var mutex = new Mutex(false, "Global\MyAppSingletonMutex");
if (!mutex.WaitOne(TimeSpan.FromSeconds(1))) // Таймаут для избежания deadlock
{
Console.WriteLine("Другой экземпляр приложения уже запущен.");
return;
}
try
{
// Код, выполняемый только в одном экземпляре приложения
}
finally
{
mutex.ReleaseMutex();
}
3. Semaphore (Ограничение количества потоков)
- Область действия: Может быть внутрипроцессным или именованным (межпроцессным).
- Доступ: Ограничивает не единицей, а заданным максимальным количеством потоков, которые могут одновременно войти в секцию.
- Использование: Для ограничения доступа к пулу ресурсов (например, не более 10 одновременных подключений к БД).
// Семафор, разрешающий одновременный доступ 3 потокам
private static SemaphoreSlim _pool = new SemaphoreSlim(3, 3);
async Task AccessDatabaseAsync()
{
await _pool.WaitAsync(); // Ждем, если уже 3 потока внутри
try
{
// Не более 3 потоков выполняют эту часть одновременно
await ExecuteQueryAsync();
}
finally
{
_pool.Release();
}
}
Сводная таблица
| Примитив | Область действия | Количество потоков | Ключевое назначение |
|---|---|---|---|
Monitor (lock) |
Внутри процесса | 1 | Быстрая синхронизация потоков в одном приложении. |
| Mutex | Внутри или между процессами | 1 | Эксклюзивный доступ к ресурсу для одного потока/процесса. |
| Semaphore | Внутри или между процессами | N (задается) | Ограничение пула одновременных операций (например, подключений, потоков). |
Вывод: Выбирайте Monitor/lock для простых случаев внутри одного приложения. Используйте Mutex для межпроцессной блокировки. Применяйте Semaphore или SemaphoreSlim, когда нужно ограничить не единицей, а заданным числом потоков.
Ответ 18+ 🔞
А, ну вот, опять эти синхронизационные штуки, про которые все путаются, как слепые котята в коробке! Слушай, давай разложу по полочкам, чтобы наконец-то дошло, в чём разница между Monitor, Mutex и Semaphore. Это ж элементарно, Ватсон, если не забивать голову лишней хуйнёй.
Monitor — это как наш местный, доморощенный засов на двери в сортире. Работает только внутри одного процесса, в нашей родной квартире. Подошёл к двери, щёлк — lock(_object) — и ты внутри. Больше никто не зайдёт, пока ты не выйдешь. Легковесный, быстрый, для своих. Просто lock — это и есть обёртка над Monitor.
private object _lockObj = new object(); // Наш засов
void UseToilet()
{
lock (_lockObj) // Щёлк — закрылся
{
// Делай что хочешь, тут только ты один
// Остальные ждут снаружи, как дурачки
}
// Выходишь — засов открывается сам
}
Mutex — это уже серьёзнее. Это как именной навесной замок на каком-нибудь общем сарае во дворе. Может работать между разными процессами (если именованный). У него есть «хозяин». Взять замок (WaitOne) может только один чувак — либо из твоего приложения, либо из соседнего. Пока он не откроет (ReleaseMutex), все остальные будут мотаться вокруг и материться. Идеально, чтобы гарантировать, что твоё приложение в единственном экземпляре запущено, например.
// Глобальный замок на весь компьютер
using var mutex = new Mutex(false, "Global\MySuperAppMutex");
// Пытаемся взять замок, ждём не больше секунды, чтобы не зависнуть нахуй
if (!mutex.WaitOne(TimeSpan.FromSeconds(1)))
{
// Не получилось — значит, другой экземпляр уже висит на этом замке
Console.WriteLine("Да отъебись, я уже работаю!");
return;
}
try
{
// Ага, я здесь хозяин, делаю что надо
// Другие и процессы, и потоки — все ждут
}
finally
{
mutex.ReleaseMutex(); // Открыл замок, теперь следующий может попробовать
}
Semaphore — это вообще отдельная песня. Представь себе турникет в метро. Их не один, а несколько. Semaphore говорит: «Одновременно внутри может быть не больше N потоков». Задал, например, 3 — и первые три прошли. Четвёртый будет ждать, пока кто-то из первых не выйдет (Release). SemaphoreSlim — его младший брат, для работы в рамках одного процесса, обычно с асинхронщиной.
Используется, когда у тебя ограниченный пул чего-то: подключений к базе, потоков на обработку файлов, мест на парковке. Чтобы всё не легло, как говно в проруби, от слишком большого наплыва.
// Сделаем семафор на 3 места, типа машин на парковке
private static SemaphoreSlim _parking = new SemaphoreSlim(3, 3);
async Task ParkCarAsync()
{
await _parking.WaitAsync(); // Ждём свободное место. Если все 3 заняты — стопимся тут
try
{
// Ура, заехали на парковку, делаем свои дела
await ProcessCarAsync();
}
finally
{
_parking.Release(); // Выехали, освободили место для следующего
}
}
Короче, сводка для тех, кто дочитал и всё равно нихуя не понял:
| Штука | Где работает | Сколько пускает | Зачем нужно |
|---|---|---|---|
Monitor (lock) |
Внутри одного процесса | Только 1 поток | Быстро и просто отгородиться от соседних потоков в своём приложении. |
| Mutex | Может и между процессами | Только 1 поток/процесс | Чтобы что-то было в единственном экземпляре на всю систему. Или для межпроцессной эксклюзивной блокировки. |
| Semaphore | Может и между процессами | N потоков (сколько скажешь) | Чтобы не пускать всех сразу, а ограничить одновременный доступ, как турникетом. |
Итог: Не усложняй. В 95% случаев внутри своего приложения тебе хватит обычного lock (то есть Monitor). Полез глобально что-то блокировать — смотри в сторону Mutex. Нужно пропускать по N штук, а не по одному — бери Semaphore или SemaphoreSlim. Всё, вопрос закрыт, можно идти пить чай.