一、自旋鎖
自旋鎖是指當一個線程在獲取鎖對象的時候,如果鎖已經被其它線程獲取,那麼這個線程將會循環等待,不斷的去獲取鎖,直到獲取到了鎖。適合於原子操作時間非常短的場景
優點:避免了線程上下文切換。性能較高。
缺點:如果長時間等待,將消耗大量的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}");
}