使用SemaphoreSlim取代lock語法

在網站程式中,當有一個資源不能由多個執行緒存取,通常都是用lock語法,但一來它較吃效能,二來它不支持async語法,也就是無法寫非同步的程式碼,這時SemaphoreSlim就是一個良好的替代方案,同時使用上也很有彈性。
因為它不是單純只能放一個執行緒進去,它是可以設定進入的執行緒數量,例如資料庫的存取,如果怕會有瞬間流量,也可以限制一次只能10個執行緒去使用資料庫。
下面是簡單範例:
public class SemaphoreTestClass
{
public static SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
private int _id = 0;
public SemaphoreTestClass(int id)
{
_id = id;
}
public async Task WaitAndDoWork()
{
Stopwatch sw = Stopwatch.StartNew();
sw.Start();
Console.WriteLine($"{_id} Waiting to proceed with the work...");
await _semaphoreSlim.WaitAsync();
Console.WriteLine("Proceeding with the work...");
Thread.Sleep(1000);
Console.WriteLine($"{_id} Work completed, releasing semaphore.");
_semaphoreSlim.Release();
sw.Stop();
Console.WriteLine($"{_id} :{sw.ElapsedMilliseconds}");
}
}
上面的類別,宣告一個SemaphoreSlim,它的參數分別是初始數量跟最大數量,通常會設一樣,同時設1,效果就跟lock一樣,也有設定(0,1)的,它是確保程式初始化完成後,執行Release,這樣執行緒才能進入。
using SemaphoreSlimTestConsoleAP;
SemaphoreSlim semaphoreSlim= new SemaphoreSlim(1, 1);
semaphoreSlim.Wait();
Console.WriteLine("Proceeding with the work...");
Thread.Sleep(1000);
semaphoreSlim.Release();
Console.WriteLine("Work completed, semaphore released.");
List<SemaphoreTestClass> list = new List<SemaphoreTestClass>
{
new SemaphoreTestClass(1),
new SemaphoreTestClass(2),
new SemaphoreTestClass(3),
new SemaphoreTestClass(4)
};
Task.WaitAll(list.Select(item => item.WaitAndDoWork()).ToArray());
Console.WriteLine("All tasks completed.");
上面是測試程式,同時宣告4個物件,同時執行,但最後都會排隊執行。
參考資料