Ответ
В C# для синхронизации доступа к общим ресурсам из нескольких потоков используется ряд примитивов. Выбор зависит от сценария:
-
lock(ключевое слово, обертка надMonitor): Стандартный выбор для синхронизации в рамках одного процесса.private readonly object _syncRoot = new object(); public void ModifySharedResource() { lock (_syncRoot) { // Критическая секция. Только один поток выполняет этот блок. sharedCounter++; } } -
Mutex(мьютекс): Подходит для межпроцессной синхронизации (например, чтобы гарантировать запуск только одного экземпляра приложения).using var mutex = new Mutex(false, "Global\MyAppSingletonMutex"); if (!mutex.WaitOne(TimeSpan.FromSeconds(5))) { Console.WriteLine("Другой экземпляр приложения уже запущен."); return; } try { /* Работа приложения */ } finally { mutex.ReleaseMutex(); } -
SemaphoreSlim/Semaphore: Ограничивают количество потоков, которые могут одновременно войти в критическую секцию.SemaphoreSlimлегче и рекомендуется для внутрипроцессной работы.private readonly SemaphoreSlim _pool = new SemaphoreSlim(2, 2); // Максимум 2 потока public async Task AccessDatabaseAsync() { await _pool.WaitAsync(); try { /* Ограниченный доступ к ресурсу (напр., соединению с БД) */ } finally { _pool.Release(); } } -
ReaderWriterLockSlim: Оптимизирован для сценариев, где чтение происходит часто, а запись редко. Позволяет множественное чтение, но эксклюзивную запись.private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim(); public string ReadData() { _rwLock.EnterReadLock(); try { return cachedData; } finally { _rwLock.ExitReadLock(); } } public void UpdateData(string newData) { _rwLock.EnterWriteLock(); try { cachedData = newData; } finally { _rwLock.ExitWriteLock(); } }
Общая рекомендация: Для простых случаев используйте lock. Для асинхронного кода — SemaphoreSlim. Для межпроцессного взаимодействия — Mutex. Для оптимизации read-heavy нагрузок — ReaderWriterLockSlim.
Ответ 18+ 🔞
А, синхронизация потоков в C#! Ну это ж классика, вечная тема, как эти ваши сериалы про врачей. Все думают, что это просто — взял lock и поехал, а потом бац — дедлок, и приложение висит, как будто его кто-то в рот ебал.
Смотри, тут главное — не выебываться с навороченными штуками, если можно проще. Вот прям как в жизни: зачем брать бензопилу, чтобы хлеб нарезать?
1. lock — твой базовый, родной, как тапки домашние.
Используешь в 90% случаев, когда нужно внутри одного процесса, чтобы потоки не налетели на общую переменную, как пьяные мухи на стекло. Просто, надёжно, нихуя лишнего.
private readonly object _syncRoot = new object(); // вот этот объект — твой ключ от сортира
public void IncrementCounter()
{
lock (_syncRoot) // захватил ключ — иди сри один
{
sharedValue++; // сделал дело — гуляй смело
}
}
2. Mutex — это когда тебе нужно не просто в своей квартире дверь закрыть, а на весь подъезд один унитаз.
Межпроцессная синхронизация, блядь. Например, чтобы второй экземпляр твоего софта не запустился и не устроил пиздец.
using var mutex = new Mutex(false, "Global\MyAppSingletonMutex");
if (!mutex.WaitOne(TimeSpan.FromSeconds(5))) // ждём пять секунд, как будто в очереди в этот самый единственный унитаз
{
Console.WriteLine("Брось, чувак, уже кто-то сидит!");
return;
}
try { /* делаем свои грязные дела */ }
finally { mutex.ReleaseMutex(); } // не забудь спустить воду и ключ вернуть, свинья!
3. SemaphoreSlim / Semaphore — это как очередь в кабинет к гинекологу.
Внутри только N человек одновременно, остальные — ждут на улице и курят. SemaphoreSlim — это лёгкая версия, для асинхронного кода прям ваще красота.
private readonly SemaphoreSlim _pool = new SemaphoreSlim(2, 2); // всего два стула, больше не влезут
public async Task AccessResourceAsync()
{
await _pool.WaitAsync(); // ждём, пока один из двух выйдет
try { /* пользуемся ресурсом, типа соединения с базой */ }
finally { _pool.Release(); } // вышли — следующий заходи
}
4. ReaderWriterLockSlim — это как библиотека.
Читать могут все, хоть толпой заходи, а вот чтобы книжку на полку поставить (записать) — всех нахуй выгоняем, делаем дело в тишине и одиночестве. Для мест, где читают часто, а пишут раз в год.
private readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim();
public string GetConfig()
{
_rwLock.EnterReadLock(); // зашли почитать, нас тут много
try { return _settings; }
finally { _rwLock.ExitReadLock(); } // ушли, не шумим
}
public void UpdateConfig(string newShit)
{
_rwLock.EnterWriteLock(); // все, пиздуйте нахуй, я сейчас важное дело делаю
try { _settings = newShit; }
finally { _rwLock.ExitWriteLock(); } // готово, можете снова толпиться
}
Итог, ёпта:
- Обычная хуйня в одном процессе?
lockи не парься. - Нужно ждать в асинхронном коде?
SemaphoreSlimтебе в руки. - Хочешь, чтобы приложение в единственном экземпляре было?
Mutex, глобальный, как совесть. - Чтения дохуя, а записи раз в полгода?
ReaderWriterLockSlim, чтобы не стоять в очереди почём зря.
А то возьмёшь не тот примитив — и потом сидишь, дебажишь эти гонки данных, волосы на жопе рвёшь. Не усложняй без нужды, работает и ладно.