在網站程式中,當有一個資源不能由多個執行緒存取,通常都是用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個物件,同時執行,但最後都會排隊執行。

參考資料