2018年8月20日 星期一

【C#】Various kinds of lock

除了標準的 lock (){} 外,還有對應不同應用的其他鎖

根據同時是否允許複數的執行緒存取 critical section 可分為 exclusive 和 shared 兩類

Exclusive
指一次只能有一個 thread 進入。
lock=Monitor、Mutex、Other EventWaitHandle

Shared
一次可以有指定數量的 threads 進入。
Semaphore、ReadWriteLock

lock and Monitor
基本上 lock(){}就是
var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}
的語法糖,如果沒有要用 Monitor.TryEnter() 設 timeout 的話都用 lock{}就可以。

Mutex
以下是用作鎖的例子並附上同屬 EventWaitHandle 的 AutoResetEvent 交互參考,基本上 Mutex 的主要用途是跨 Processes 同步,沒有此需求就不要用它浪費資源了。
//static AutoResetEvent _ewh = new AutoResetEvent(true);
static Mutex _mut = new Mutex();
static bool _taken = false;

static void Main()
{
    for (int i = 0; i < 5; i++)
    {
        Task.Run(async () =>
        {
            await Task.Delay(new Random().Next(100, 500));
            Take();
        });
    }

    Console.ReadLine();
}

private static void Take()
{
    //_ewh.WaitOne();
    _mut.WaitOne();
    if (_taken)
    {
        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} attempt failed, resource was taken");
    }
    else
    {
        _taken = true;
        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} took the resource");
    }

    //_ewh.Set();
    _mut.ReleaseMutex();
}

Semaphore
這個跟 ReadWriteLock 都有 Slim 版,Slim 版不能跨 Processes,但資源消耗較小,一般會用 Slim 版。 用法類似 WaitHandle,但建構式處可指定進入緒的數量。
static SemaphoreSlim sphs = new SemaphoreSlim(3);
static BlockingCollection<string> resources = new BlockingCollection<string>() { "res1", "res2", "res3", "res4", "res5", "res6", "res7", "res8", "res9" };

static void Main()
{
    var resCount = resources.Count;
    for (int i = 0; i < resCount; i++)
    {
        Task.Run(async () =>
        {
            await Take();
        });
    }

    Console.ReadLine();
}

private async static Task Take()
{
    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} is waiting");
    sphs.Wait();
    if (resources.Any())
    {
        var res = resources.Take();
        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} takes the {res}");

    }
    else
    {
        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} attempt failed, all resources was taken");
    }
    await Task.Delay(new Random().Next(500, 1000));

    Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} released");
    sphs.Release();
}
我一直覺得 WaitHandle 這個家族的用法語意很差,但不知道為什麼很多類都是參考這個風格做的,搞得多緒每次寫每次查。

ref: 亞斯狼(鎖差別一覽表)


沒有留言:

張貼留言