📜  C#线程同步(1)

📅  最后修改于: 2023-12-03 15:00:17.553000             🧑  作者: Mango

C#线程同步

在多线程编程中,为了避免线程间的竞争条件(Race Condition)和死锁(Deadlock),需要使用线程同步的技术。C#提供了多种线程同步的方式,包括锁(lock)、互斥量(Mutex)、事件(Event)、信号量(Semaphore)等。

锁(lock)

锁是最常见的线程同步方式。C#中可以通过lock关键字来实现锁。锁保证在同一时间只有一个线程可以访问指定代码块。

class Program
{
    static object locker = new object();
    
    static void Main(string[] args)
    {
        Thread t1 = new Thread(DoWork);
        Thread t2 = new Thread(DoWork);
        t1.Start();
        t2.Start();
        Console.ReadLine();
    }

    static void DoWork()
    {
        lock (locker)
        {
            Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
            Thread.Sleep(1000);
            Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
        }
    }
}

在上面的例子中,我们使用lock关键字来实现对 DoWork 方法的同步。当一个线程进入该方法时,它会获取锁,执行指定代码块,然后释放锁。其他线程进入该方法时,必须等待前一个线程释放锁后才可以执行。

互斥量(Mutex)

互斥量也可以用来实现线程同步。和锁不同的是,互斥量可以被跨进程地共享访问。

class Program
{
    static Mutex mutex = new Mutex();

    static void Main(string[] args)
    {
        Thread t1 = new Thread(DoWork);
        Thread t2 = new Thread(DoWork);
        t1.Start();
        t2.Start();
        Console.ReadLine();
    }

    static void DoWork()
    {
        mutex.WaitOne();
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
        Thread.Sleep(1000);
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
        mutex.ReleaseMutex();
    }
}

在上面的例子中,我们使用Mutex类来实现对 DoWork 方法的同步。一个线程在获取互斥量后才可执行指定代码块,执行完后又释放互斥量,其他线程才能继续执行。

事件(Event)

事件也是一种常见的线程同步方式。和互斥量不同的是,事件可以在多个线程之间传递信号,用于同步事件的发生和处理。

class Program
{
    static AutoResetEvent autoEvent = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        Thread t1 = new Thread(() =>
        {
            Thread.Sleep(2000);
            Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " starts signal.");
            autoEvent.Set();
        });
        Thread t2 = new Thread(DoWork);

        t1.Start();
        t2.Start();

        Console.ReadLine();
    }

    static void DoWork()
    {
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " waiting signal.");
        autoEvent.WaitOne();
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " received signal and start.");
        Thread.Sleep(1000);
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
    }
}

在上面的例子中,我们使用AutoResetEvent自动重置事件类来实现对 DoWork 方法的同步。一个线程在调用 WaitOne 方法时等待事件的发生,当事件被触发时调用 Set 方法来通知其他线程,然后自动重置事件。其他线程在调用 WaitOne 方法时等待事件的发生,当事件被触发时就可以执行指定代码块。

信号量(Semaphore)

信号量用于控制同时执行的线程数量。通过控制信号量的初始值和每次释放的数量,我们可以控制允许执行的线程数量。

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

    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            Thread t = new Thread(DoWork);
            t.Start();
        }
        Console.ReadLine();
    }

    static void DoWork()
    {
        sem.WaitOne();
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " start.");
        Thread.Sleep(1000);
        Console.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " end.");
        sem.Release();
    }
}

在上面的例子中,我们使用Semaphore类来实现对 DoWork 方法的同步。初始值和每次释放的数量都设为2,因此最多只能有两个线程同时执行指定代码块。当一个线程获取到信号量后,执行指定代码块,然后释放信号量让其他线程获取。