C#线程锁(自旋锁SpinLock、互斥锁Mutex、混合锁Monitor | lock)

一、自旋锁

自旋锁是指当一个线程在获取锁对象的时候,如果锁已经被其它线程获取,那么这个线程将会循环等待,不断的去获取锁,直到获取到了锁。适合于原子操作时间非常短的场景

优点:避免了线程上下文切换。性能较高。

缺点:如果长时间等待,将消耗大量的CPU资源。而且多个等待中的线程,并不是等待时间越长就先获取到锁,可能会一直等待下去。

两种实现方式如下:

实现代码一:

private static int _SpinLock = 0;//锁对象
private static int incrValue = 0;//共享资源
private void Run()
{
    //获取锁
    //使用int原子操作将_SpinLock的值赋值为1,Interlocked.Exchange(ref _SpinLock, 1)的返回值为改变之前的值。
    //如果返回0,则获取到了锁, 如果返回1,则锁被占用
    while (Interlocked.Exchange(ref _SpinLock, 1) != 0)
    {
        Thread.SpinWait(1);//自旋锁等待
    }

    incrValue++;  //安全的逻辑计算

    //释放锁:将_SpinLock重置会0;
    Interlocked.Exchange(ref _SpinLock, 0);
}

[HttpGet]
public async Task<string> Get()
{
    Parallel.For(0, 1000, (i) =>
    {
        Run();
    });

    Console.WriteLine($"incrValue={incrValue}");
}

实现代码二:

private static SpinLock _spinLock = new SpinLock();
private static int incrValue = 0;//共享资源
private void Run()
{
    bool locked = false;
    _spinLock.Enter(ref locked);//获取锁

    incrValue++;  //安全的逻辑计算

    if (locked) //释放锁
        _spinLock.Exit();
}
[HttpGet]
public async Task<string> Get()
{
    Parallel.For(0, 1000, (i) =>
    {
        Run();
    });

    Console.WriteLine($"incrValue={incrValue}");
}

 

二、互斥锁

互斥锁是基于原子操作和线程调度实现的;当一个线程在获取锁对象的时候,如果锁已经被其它线程获取,那么这个线程不会循环获取锁,它会进入等待状态,等待被唤醒。适用于等待时间较长和跨进程的场景。

互斥锁支持重入(当一个线程获取到锁之后,中间的代码可以再次获取锁。适用于多个函数调用。获取锁的次数必须等于释放锁的次数)。

互斥锁支持跨进程共享;多个进程之间使用同一个互斥锁。

实现代码如下:

private static readonly Mutex _mutexLock = new Mutex();
private static int incrValue = 0;//共享资源
private void Run()
{
    _mutexLock.WaitOne();//获取锁
    try
    {
        incrValue++;  //安全的逻辑计算
    }
    finally {
        _mutexLock.ReleaseMutex();//释放锁
    }
}

[HttpGet]
public async Task<string> Get()
{

    Parallel.For(0, 1000, (i) =>
    {
        Run();
    });

    Console.WriteLine($"incrValue={incrValue}");
}

 

三、混合锁

混合锁混合了自旋锁和互斥锁。刚开始会像自旋锁一样,先重试一定的次数;超过这个次数之后将线程设置为等待状态。

混合锁适用于大多数场景。

实现代码一:

 private static readonly object _monitorLock = new object();
 private static int incrValue = 0;//共享资源
 private void Run()
 {
     var islocked = false;
     try
     {
         Monitor.Enter(_monitorLock, ref islocked);  //获取锁

         incrValue++;  //安全的逻辑计算
     }
     finally {
       if(islocked) Monitor.Exit(_monitorLock);// 释放锁
     }
 }

 [HttpGet]
 public async Task<string> Get()
 {
     Parallel.For(0, 1000, (i) =>
     {
         Run();
     });

     Console.WriteLine($"incrValue={incrValue}");
}

实现代码二:

private static readonly object _monitorLock = new object();
private static int incrValue = 0;//共享资源
private void Run()
{
    lock (_monitorLock)
    {
        incrValue++;  //安全的逻辑计算
    }
}

[HttpGet]
public async Task<string> Get()
{
    Parallel.For(0, 1000, (i) =>
    {
        Run();
    });

    Console.WriteLine($"incrValue={incrValue}");
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章