C#线程锁(自旋锁SpinLock、互斥锁Mutex、混合锁Monitor | lock)
一、自旋锁自旋锁是指当一个线程在获取锁对象的时候,如果锁已经被其它线程获取,那么这个线程将会循环等待,不断的去获取锁,直到获取到了锁。适合于原子操作时间非常短的场景优点:避免了线程上下文切换。性能较高。缺点:如果长时间等待,将消耗大量的CPU资源。而且多个等待中的线程,并不是等待时间越长就先获取到锁,有可能会一直等待下去。两种实现方式如下:实现代码一:private static int _Spi
一、自旋锁
自旋锁是指当一个线程在获取锁对象的时候,如果锁已经被其它线程获取,那么这个线程将会循环等待,不断的去获取锁,直到获取到了锁。适合于原子操作时间非常短的场景
优点:避免了线程上下文切换。性能较高。
缺点:如果长时间等待,将消耗大量的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}");
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)