iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 6
1
Software Development

從零開始土炮MQ系列 第 6

三、Queue 的應用(2) - 以 Lock 保護共用資源

當同個時間點,生產者一邊在產生資源,並將其填入 Queue ;另一邊,消費者從 Queue 中取出資源,以利後續處理。

一來一往之處,如果沒有同步的機制,極有可能造成存取的錯亂,嚴重影響資料的正確性。

首先上場的是 .Net Lock 機制,利用指定指定物件的互斥鎖定,以確保鎖定期間,鎖定範圍內的陳述式不受其他執行緒影響,正常的完成任務,直到離開鎖定範圍

https://ithelp.ithome.com.tw/upload/images/20190922/20107551kmDNFGTW4r.png

在上面的示意圖中,兩名生產者與一名消費者,先後存取 Queue,在 Lock 機制的保護下,Queue 會依據請求時間的先後,依序完成 producer 1Customer 1Producer 2 的存取動作。

lock 的使用上,MSDN 之中,也特別提到同步處理執行緒對共用資源的存取時,請鎖定專用物件執行個體

也就是用來用 lock 的指定物件,盡可能使用專門的物件。

private readonly object _lock = new object ();

lock(_lock)
{
    //	要確保資料正確性旳程式區塊
}

同時為避免死鎖(deadlock)鎖定爭用(lock contention) 的情況發生,請避免...

  • 避免對不同的共用資源使用相同的鎖定物件。
  • 避免 thisType 類別字串

如果有興趣進一步了解,可以進一步閱讀 說說lock到底鎖誰? 這篇文章。

接著,我們繼承前面實作的 Queue ,將其加上 Lock 機制。

public class LockQueue<T> : Queue<T> 
{
    private readonly object _lock = new object ();

    public override void Enqueue (T item) 
    {
        lock (_lock) { base.Enqueue (item); }
    }

    public override bool IsEmpty => { lock (_lock) { base.IsEmpty; } }

    public T Dequeue () 
    {
        lock (_lock) { return base.Dequeue (); }
    }
}

測試程式如下:

 private static void Main(string[] args)
 {
     var queue = new Queue<int>();
     Random rand = new Random();

     var timesLimit = 10;
     var producer = Task.Run(() =>
       {
           var times = timesLimit;
           while (times-- != 0)
           {
               var item = rand.Next(500);
               System.Console.WriteLine($"Enqueue: {item}");
               queue.Enqueue(item);
               Thread.Sleep(100);
           }

           System.Console.WriteLine("produce end.");
       });

     var consumer1 = Task.Run(() => { Program.Consumer(queue); });
     var consumer2 = Task.Run(() => { Program.Consumer(queue); });

     System.Console.Read();
 }

 private static void Consumer<T>(Queue<T> queue)
 {
     while (true)
     {
         if (queue.IsEmpty)
         {
             Thread.Sleep(10);
             continue;
         }

         var item = queue.Dequeue();
         System.Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Dequeue: {item}");
         Thread.Sleep(100);
     }
 }

上一篇
三、Queue 的應用(1) - 生產者與消費者模型 (producer-consumers pattern)
下一篇
三、Queue 的應用(3) - Semaphore、Mutex的同步
系列文
從零開始土炮MQ30

尚未有邦友留言

立即登入留言