C#里有哪些线程同步的方法

发布时间 2023-05-09 08:50:22作者: yhm138

除了lock和Semaphore之外,C# 还有其他的线程同步方法,如 Monitor, Mutex, ReaderWriterLockSlim 和 ManualResetEvent等。

在常见的编程语言中,同步原语可以分类为哪些?

关于锁的名词解释

放弃名词解释——看这篇文章了解锁的分类 https://juejin.cn/post/7010305230256488485

锁可重入(Reentrant)是指一个线程可以多次获得同一个锁,而不会产生死锁。当一个线程已经拥有了一个锁,再次尝试获得相同的锁时,该线程可以继续执行,而不会阻塞等待锁释放。

自旋锁(SpinLock)是一种特殊类型的锁,当一个线程试图获得已被其他线程持有的锁时,不会立即进入阻塞状态。相反,该线程会在一个循环中忙等待(busy-waiting),不断地检查锁是否可用。自旋锁是一种低级别的同步原语,通常在需要极低延迟和高性能的场景下使用。

lock

优点:简单易用,适用于大多数场景,可重入。
缺点:无法手动释放锁,无法跨进程使用。

using System;
using System.Threading;

class Example
{
    private static object syncObj = new object();
    private static int counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            lock (syncObj)
            {
                counter++;
            }
        }
    }
}

Semaphore

优点:可以控制同时访问资源的线程数量,可跨进程使用。
缺点:相对于lock,使用起来稍复杂。

using System;
using System.Threading;

class Example
{
    private static Semaphore sem = new Semaphore(1, 1); // Initial count: 1, Maximum count: 1
    private static int counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            sem.WaitOne();
            counter++;
            sem.Release();
        }
    }
}

Monitor

优点:灵活性高,可手动控制锁的获取和释放,可重入。
缺点:使用起来较复杂,容易出错,无法跨进程使用。

using System;
using System.Threading;

class Example
{
    private static object syncObj = new object();
    private static int counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            bool lockTaken = false;
            try
            {
                Monitor.Enter(syncObj, ref lockTaken);
                counter++;
            }
            finally
            {
                if (lockTaken)
                {
                    Monitor.Exit(syncObj);
                }
            }
        }
    }
}

Mutex

优点:可跨进程使用,可重入。
缺点:性能相对较差,开销较大,相对于lock和Monitor,使用起来较复杂。

using System;
using System.Threading;

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

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            mutex.WaitOne();
            counter++;
            mutex.ReleaseMutex();
        }
    }
}

ReaderWriterLockSlim

优点:读写锁定分离,适用于读操作远多于写操作的场景,性能较高。
缺点:使用起来相对复杂,不可跨进程使用。

using System;
using System.Threading;

class Example
{
    private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    private static int counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        for (int i = 0; i < 1000; i++)
        {
            rwLock.EnterWriteLock();
            counter++;
            rwLock.ExitWriteLock();
        }
    }
}

ManualResetEvent

优点:灵活,可以用于多个线程之间的信号传递。
缺点:不是传统意义上的同步锁,只能控制线程的等待和继续,不适用于保护资源的互斥访问。

using System;
using System.Threading;

class Example
{
    private static ManualResetEvent mre = new ManualResetEvent(false);
    private static int counter = 0;

    static void Main()
    {
        Thread t1 = new Thread(IncrementCounter);
        Thread t2 = new Thread(IncrementCounter);

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

        mre.Set();

        t1.Join();
        t2.Join();

        Console.WriteLine("Counter: " + counter);
    }

    static void IncrementCounter()
    {
        mre.WaitOne();

        for (int i = 0; i < 1000; i++)
        {
            counter++;
        }
    }
}

请注意,这些代码示例中的同步方法各有优缺点。在实际应用中,请根据具体需求选择合适的同步方法。

在实际应用中,需要根据需求和场景来选择合适的同步方法。

  • 简单场景下,可以优先考虑使用lock。
  • 如果需要跨进程同步,可以使用Mutex或Semaphore。
  • 在读写操作不平衡的情况下,可以考虑使用ReaderWriterLockSlim。
  • 对于线程之间的信号传递,可以使用ManualResetEvent。
  • 而在需要更多控制和灵活性的场景下,可以考虑使用Monitor。