iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 8
1
Software Development

從零開始土炮MQ系列 第 8

三、Queue 的應用(4) - ManualResetEvent 與 Lock 的 BlockQueue 補完

  • 分享至 

  • xImage
  •  

3.4 ManualResetEvent

使用 semaphoreMutex 可以達到對 BlockQueue 的要求。單純使用 Lock ,能確保共用資源操作的原子性,卻無法達到通知生產者或消費者的目的。

所以接下來,來使用 .Net 之中,可以使用基於 EventWaitHandleManualResetEventManualResetEventSlim,達到通知 BlockQueue 的特性。

ManualResetEvent vs AutoResetEvent

可能會有人有疑問,AutoResetEvent 一樣是繼承 EventWaitHandle ,為何選擇 ManualResetEvent ,而不使用AutoResetEvent ?

ManualResetEvent AutoResetEvent
若鎖定後,要再次使用前,必須先重置狀態。 若鎖定後,使用時,會自行重置狀態。
一次可以釋放所有鎖定的執行緒。 一次只能釋放一個執行緒。

而在 BlockQueue 之中,除非特別去記錄執行緒資訊,不然無法明確得知執行緒的數量。使用ManualRestEvent可以一口氣釋放所有的執行緒,減少一筆筆釋放的麻煩。

ManualResetEventSlim

同時,在 .Net Core 之中,還有一個 ManualRestEventSlim的類別提供使用,它可視為輕量化的 ManualRestEvent

據官方的說法,ManualResetEventSlim的執行效能高於 ManualRestEvent,但是兩者間還是有些差異。

  • ManualResetEventSlim 只能在同一個 Process 範圍內作用;ManualResetEvent則沒有這個限制。
  • ManualResetEventSlim 的短時間的鎖定時,所花費的成本較低。

實作

public class BlockQueue<T> : LockQueue<T>
{
    private ManualResetEventSlim _enqueueWait;
    private ManualResetEventSlim _dequeueWait;

    public BlockQueue()
    {
        _enqueueWait = new ManualResetEventSlim(false);
        _dequeueWait = new ManualResetEventSlim(false);
    }

    public void Enqueue(T item)
    {
        while (true)
        {
        	// 因為 LockQueue 的上限是 20, 偷懶寫法。
            if (this.Count == 20)
            {
                _enqueueWait.Wait();
            }

            base.Enqueue(item);
            //	準備 Enqueue 下次的 Wait 
            _enqueueWait.Reset();
            
            //	Dequeue 放行
            _dequeueWait.Set();
        }
    }

    public T Dequeue()
    {
        while (true)
        {
            if (base.IsEmpty == true)
            {
                _dequeueWait.Wait();
            }

            var dequeue = base.Dequeue();
            _dequeueWait.Reset();
            _enqueueWait.Set();
            return dequeue;
        }
    }
}

測試程式,請見 Lock 章節。

小結

不管是 LockSemaphoreMutexManualReset,提到的都概念與簡單的實作練習。在 .Net Core 之中,己經針對生產者消費者模型,設計 IProducerConsumerCollection 介面。

BlockCollectionConcurrentQueue 均為IProducerConsumerCollection的實作,提供安全執行緒集合適用的封鎖和界限容量,

也因為 .Net core 之中,己經提供 System.Collections.Concurrent.ConcurrentQueue 這個更加完整的 Queue ,在往後的實作,改為使用 ConcurrentQueue

延伸閱讀

  1. 生產者 vs 消費者 - BlockQueue 實作
  2. [C#.NET][Thread] 執行緒同步機制 - AutoResetEvent / ManualResetEvent

上一篇
三、Queue 的應用(3) - Semaphore、Mutex的同步
下一篇
三、Queue 的應用(5) - 名詞補完:原子性、有序性、可見性
系列文
從零開始土炮MQ30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言