Приведи пример использования примитива синхронизации
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Пример использования примитива синхронизации в C#
В C# для синхронизации доступа к общим ресурсам в многопоточных приложениях используются различные примитивы синхронизации. Я продемонстрирую практический пример с использованием Mutex (взаимоисключающая блокировка) — одного из фундаментальных примитивов синхронизации.
Контекст проблемы
Представьте ситуацию, где несколько потоков одновременно пытаются записывать данные в один файл. Без синхронизации это приведёт к состоянию гонки (race condition), повреждению данных и непредсказуемому поведению.
Пример с использованием Mutex
using System;
using System.Threading;
using System.IO;
class Program
{
// Создаём именованный Mutex для синхронизации между потоками
private static Mutex fileMutex = new Mutex(false, "Global\\FileWriteMutex");
private static string filePath = "shared_log.txt";
static void Main()
{
// Запускаем несколько потоков для записи в файл
Thread[] threads = new Thread[5];
for (int i = 0; i < threads.Length; i++)
{
int threadId = i + 1;
threads[i] = new Thread(() => WriteToFile($"Thread {threadId}: Message {DateTime.Now:HH:mm:ss.fff}"));
threads[i].Start();
// Небольшая задержка для эмуляции реалистичного сценария
Thread.Sleep(50);
}
// Ожидаем завершения всех потоков
foreach (var thread in threads)
{
thread.Join();
}
Console.WriteLine("Все потоки завершили работу.");
Console.WriteLine($"Содержимое файла {filePath}:");
Console.WriteLine(File.ReadAllText(filePath));
// Освобождаем ресурсы Mutex
fileMutex.Close();
}
static void WriteToFile(string message)
{
try
{
// Захватываем блокировку
fileMutex.WaitOne();
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} получил доступ к файлу");
// Критическая секция - запись в файл
using (StreamWriter writer = File.AppendText(filePath))
{
writer.WriteLine(message);
writer.Flush();
// Имитация обработки
Thread.Sleep(100);
}
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} завершил запись");
}
finally
{
// Всегда освобождаем блокировку
fileMutex.ReleaseMutex();
}
}
}
Ключевые моменты реализации
-
Инициализация Mutex:
- Создаём именованный
Mutexс префиксомGlobal\\, что позволяет синхронизировать процессы в рамках всей системы - Без имени
Mutexбудет работать только в рамках одного процесса
- Создаём именованный
-
Критическая секция:
WaitOne()блокирует текущий поток до получения доступа к ресурсу- Операции внутри блока
try-finallyзащищены от одновременного выполнения ReleaseMutex()освобождает блокировку для других потоков
-
Обработка ошибок:
- Использование
try-finallyгарантирует освобождениеMutexдаже при возникновении исключений - Без этого дедлок (взаимная блокировка) был бы неизбежен
- Использование
Альтернативные примитивы синхронизации
Помимо Mutex, в C# доступны:
lock(монитор) — для синхронизации в пределах одного процесса:
private static readonly object lockObject = new object();
lock(lockObject)
{
// Критическая секция
}
Semaphore— ограничивает количество одновременных доступов:
private static SemaphoreSlim semaphore = new SemaphoreSlim(3, 3);
await semaphore.WaitAsync();
try { /* работа с ресурсом */ }
finally { semaphore.Release(); }
ReaderWriterLockSlim— оптимизирован для сценариев с частым чтением:
private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
// Для чтения
rwLock.EnterReadLock();
try { /* чтение данных */ }
finally { rwLock.ExitReadLock(); }
// Для записи
rwLock.EnterWriteLock();
try { /* изменение данных */ }
finally { rwLock.ExitWriteLock(); }
Практические рекомендации
- Минимизируйте время удержания блокировки — выполняйте в критической секции только необходимые операции
- Избегайте вложенных блокировок — это частая причина дедлоков
- Используйте
try-finallyдля гарантированного освобождения ресурсов - Выбирайте примитив под задачу —
Mutexдля межпроцессного взаимодействия,lockдля внутрипроцессной синхронизации - Рассмотрите асинхронные альтернативы —
SemaphoreSlim.WaitAsync()для асинхронного кода
Данный пример демонстрирует базовый паттерн использования Mutex для защиты разделяемого ресурса от конкурентного доступа, что является фундаментальным аспектом разработки многопоточных приложений на C#.