Способы синхронизации потоков (Semaphore, Mutex, lock, Monitor) в C#

Синхронизация потоков в C# необходима для предотвращения состояния гонки и обеспечения корректного доступа к общим ресурсам. В языке предусмотрены различные механизмы синхронизации, такие как lock, Monitor, Mutex и Semaphore.

Использование lock

lock используется для простого контроля доступа к разделяемому ресурсу внутри одного процесса.

using System;
using System.Threading;

class Program
{
    private static object lockObject = new object();
    private static int counter = 0;

    static void Increment()
    {
        lock (lockObject)
        {
            counter++;
            Console.WriteLine($"Counter: {counter}");
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(Increment);
        Thread t2 = new Thread(Increment);
        t1.Start();
        t2.Start();
        t1.Join();
        t2.Join();
    }
}

Использование Monitor

Monitor предоставляет более гибкий механизм синхронизации, чем lock, позволяя вручную управлять входом и выходом из критической секции.

using System;
using System.Threading;

class Program
{
    private static object monitorObject = new object();
    private static int counter = 0;

    static void Increment()
    {
        bool lockTaken = false;
        try
        {
            Monitor.Enter(monitorObject, ref lockTaken);
            counter++;
            Console.WriteLine($"Counter: {counter}");
        }
        finally
        {
            if (lockTaken)
                Monitor.Exit(monitorObject);
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(Increment);
        Thread t2 = new Thread(Increment);
        t1.Start();
        t2.Start();
        t1.Join();
        t2.Join();
    }
}

Использование Mutex

Mutex позволяет синхронизировать потоки даже между разными процессами.

using System;
using System.Threading;

class Program
{
    private static Mutex mutex = new Mutex();
    private static int counter = 0;

    static void Increment()
    {
        mutex.WaitOne();
        try
        {
            counter++;
            Console.WriteLine($"Counter: {counter}");
        }
        finally
        {
            mutex.ReleaseMutex();
        }
    }

    static void Main()
    {
        Thread t1 = new Thread(Increment);
        Thread t2 = new Thread(Increment);
        t1.Start();
        t2.Start();
        t1.Join();
        t2.Join();
    }
}

Использование Semaphore

Semaphore управляет количеством потоков, которым разрешен доступ к ресурсу одновременно.

using System;
using System.Threading;

class Program
{
    private static Semaphore semaphore = new Semaphore(2, 2);

    static void AccessResource()
    {
        semaphore.WaitOne();
        try
        {
            Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} выполняет работу");
            Thread.Sleep(1000);
        }
        finally
        {
            semaphore.Release();
        }
    }

    static void Main()
    {
        for (int i = 0; i < 5; i++)
        {
            new Thread(AccessResource).Start();
        }
    }
}

Для корректной работы многопоточных программ в C# важно правильно выбирать механизм синхронизации. lock и Monitor подходят для простых сценариев, Mutex используется для межпроцессной синхронизации, а Semaphore позволяет ограничивать количество потоков, имеющих доступ к ресурсу одновременно.