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(){}就是
  1. var temp = obj;
  2. Monitor.Enter(temp);
  3. try
  4. {
  5. body
  6. }
  7. finally
  8. {
  9. Monitor.Exit(temp);
  10. }
的語法糖,如果沒有要用 Monitor.TryEnter() 設 timeout 的話都用 lock{}就可以。

Mutex
以下是用作鎖的例子並附上同屬 EventWaitHandle 的 AutoResetEvent 交互參考,基本上 Mutex 的主要用途是跨 Processes 同步,沒有此需求就不要用它浪費資源了。
  1. //static AutoResetEvent _ewh = new AutoResetEvent(true);
  2. static Mutex _mut = new Mutex();
  3. static bool _taken = false;
  4.  
  5. static void Main()
  6. {
  7. for (int i = 0; i < 5; i++)
  8. {
  9. Task.Run(async () =>
  10. {
  11. await Task.Delay(new Random().Next(100, 500));
  12. Take();
  13. });
  14. }
  15.  
  16. Console.ReadLine();
  17. }
  18.  
  19. private static void Take()
  20. {
  21. //_ewh.WaitOne();
  22. _mut.WaitOne();
  23. if (_taken)
  24. {
  25. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} attempt failed, resource was taken");
  26. }
  27. else
  28. {
  29. _taken = true;
  30. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} took the resource");
  31. }
  32.  
  33. //_ewh.Set();
  34. _mut.ReleaseMutex();
  35. }

Semaphore
這個跟 ReadWriteLock 都有 Slim 版,Slim 版不能跨 Processes,但資源消耗較小,一般會用 Slim 版。 用法類似 WaitHandle,但建構式處可指定進入緒的數量。
  1. static SemaphoreSlim sphs = new SemaphoreSlim(3);
  2. static BlockingCollection<string> resources = new BlockingCollection<string>() { "res1", "res2", "res3", "res4", "res5", "res6", "res7", "res8", "res9" };
  3.  
  4. static void Main()
  5. {
  6. var resCount = resources.Count;
  7. for (int i = 0; i < resCount; i++)
  8. {
  9. Task.Run(async () =>
  10. {
  11. await Take();
  12. });
  13. }
  14.  
  15. Console.ReadLine();
  16. }
  17.  
  18. private async static Task Take()
  19. {
  20. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} is waiting");
  21. sphs.Wait();
  22. if (resources.Any())
  23. {
  24. var res = resources.Take();
  25. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} takes the {res}");
  26.  
  27. }
  28. else
  29. {
  30. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} attempt failed, all resources was taken");
  31. }
  32. await Task.Delay(new Random().Next(500, 1000));
  33.  
  34. Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} released");
  35. sphs.Release();
  36. }
我一直覺得 WaitHandle 這個家族的用法語意很差,但不知道為什麼很多類都是參考這個風格做的,搞得多緒每次寫每次查。

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


沒有留言:

張貼留言