# 執行緒控制機制 AutoResetEvent ###### tags: `C#` ## 學習目標 了解AutoResetEvent如何控制執行序內的的執行的等待或執行。 ## 一、前言 因專案需求想要將資料塞入Queue之後再去檢查內部使否有資料有則處理相關動作,因塞入Queue的時間點為多緒執行,導致會多重觸發檢查,此時可以使用 Thread 加 AutoResetEvent 的方式去處理此流程。 ## 二、範例演練 在程式啟動時直接<code>new Thread</code>並啟動,內部使用<code>_autoWait.WaitOne()</code>的方式去等待程序的觸發,觸發後會再執行<code>Queue</code>內部檢查。 ```csharp= // 新建一個 AutoResetEvent 並且初始值設定為 false var _autoWait = new AutoResetEvent(false); // 多執行緒的Queue var myQueue = new ConcurrentQueue<int>(); // 直接執行一個緒的流程 new Thread(() => { Thread.CurrentThread.IsBackground = true; while (_autoWait.WaitOne()) { Console.WriteLine($"Run Queue check, Thread ID is '{Thread.CurrentThread.ManagedThreadId}'"); while (myQueue.IsEmpty == false) { Thread.Sleep(1000); myQueue.TryDequeue(out int res); Console.WriteLine(res); } } }).Start(); var quIndex = 1; Console.WriteLine("press 'R' to play"); var readKey = Console.ReadKey(true); // 按 R 可以重複執行,確保該 Thread不會異常 while (readKey.Key == ConsoleKey.R) { Parallel.For(0, 5, index => { // 塞入 Queue 之後直接去觸法檢查行為 myQueue.Enqueue(quIndex); Console.WriteLine($"Enqueue {quIndex++} Then Call WaitSet"); _autoWait.Set(); }); Console.WriteLine("On readKey"); readKey = Console.ReadKey(true); } ``` 執行結果: 結果能發現儘管我多次去觸發,該檢檢查行為也只會進行一次,但這邊執行時還有一些小問題,會發現 "**Run Queue check,....**" 執行了兩次,目前只能說在執行當下被觸發時會直接成為下一次執行的狀態,不過因為影響不大就暫不處理了。 ![](https://i.imgur.com/eDAtuuC.png) 此外如果想要在流程內確保執行續正常運作,可以新增<code>Thread</code>並確認和操作其狀態。 ```csharp= public abstract class QueuesTask<T> { private readonly AutoResetEvent _autoWait; private readonly ConcurrentQueue<T> _queue; private Thread _threadDequeue; // 子程序要實作提出時的行為 protected abstract void DequeueProcess(T data); public QueuesTask() { _queue = new(); _autoWait = new AutoResetEvent(false); _threadDequeue = new(DequeueTask); RunDequeueTask(); } // 使用Lock確保執行緒不會被平行程式重複觸發 private object _lock = new object(); private void RunDequeueTask() { lock (_lock) { // 未啟動則啟動程序 if (_threadDequeue.ThreadState == ThreadState.Unstarted) _threadDequeue.Start(); // 程序停止則再次新增一次 else if (_threadDequeue.IsAlive == false && _threadDequeue.ThreadState == ThreadState.Stopped) { _threadDequeue = new(DequeueTask); _threadDequeue.IsBackground = true; _threadDequeue.Start(); } } } private void DequeueTask() { while (_autoWait.WaitOne()) { while (_queue.IsEmpty == false) { _queue.TryDequeue(out T obj); if (obj != null) DequeueProcess(obj); } } } public void Enqueue(T obj) { _queue.Enqueue(obj); _autoWait.Set(); // 這邊先基本檢查避免每個平行程序都要走lock if (_threadDequeue.IsAlive == false) RunDequeueTask(); } } ``` ## 結論 除了文章介紹的功能之外還有另一種控制方式 ManualResetEvent ,差別在於此操作可以一次啟動多個執行緒,細節演示結果可參考文件來源。 --- ### 相關參考來源: [執行緒同步機制](https://dotblogs.com.tw/yc421206/2011/01/05/20609) <style> code > span{ color: red;} </style>