[C#][Thread] 控制執行緒 開始/暫停/恢復/停止 的方法

[C#][Thread] 控制執行緒 開始/暫停/恢復/停止 的方法

前言:
最近BOSS叫我試著開發Android版本的溫溼度監控系統,他打算年底可以上線去賣
外加我身上還有一些CASE要做,所以一陣子沒上來寫筆記
不過我還是個小菜鳥... Andorid對我來說還有點吃力
可能之後會新增一些Xamarin的技巧文章吧,目前還在研究(汗)
今天抽空來整理一下我覺得還不錯用的執行緒操作用法

平常我們使用執行緒的時候並不會特別想要管控正在Run的執行緒

通常是等執行緒做完他該做的即會自動釋放資源

但如果像我做的監控軟體這樣,要不斷的向測點撈取數值的話

那麼管理執行緒,不讓他因使用者的操作而暴走拖垮效能就顯得特別重要

以下是我常用的操作法,如果有更好的做法也請跟我說,感謝

首先,先新增一個ThreadWork類別

話不多說,先貼上程式碼後再解釋

public class ThreadWorker
    {
        Thread _thread;
        ManualResetEvent _shutdownEvent;
        ManualResetEvent _pauseEvent;

        public void ThreadJob()
        {
            while (true)
            {
                // pause event : if WaitHandle is false, it will wait and return false
                _pauseEvent.WaitOne(Timeout.Infinite);
                // stop event
                if (_shutdownEvent.WaitOne(0)) break;

                // work here
                Console.WriteLine(DateTime.Now);

                // like Thread.Sleep(1000), but higher efficiency
                SpinWait.SpinUntil(() => false, 1000);
            }

            Console.WriteLine("Work is completed or stopped");
        }
        
        public void Start()
        {
            Console.WriteLine("Add a new thread");
            _thread = new Thread(ThreadJob);
            _shutdownEvent = new ManualResetEvent(false);
            _pauseEvent = new ManualResetEvent(true);

            _thread.IsBackground = true;
            _thread.Start();
        }

        public void Pause()
        {
            // Set WaitHandle false
            _pauseEvent.Reset();
        }

        public void Resume()
        {
            // Set WaitHandle true
            _pauseEvent.Set();
        }

        public void Stop()
        {
            // trigger stop
            _shutdownEvent.Set();
            // if thread suspend, let it resume.
            _pauseEvent.Set();
            _thread.Join();
            _thread = null;
        }
    }

簡單的來說,就是在一個無窮迴圈中加入【工作事項】跟【暫停(等待)】和【停止(跳出)】事件

ManualResetEvent _ManualResetEvent = new ManualResetEvent(bool);

參數 bool值 可視為設定ManualResetEvent類別中的WaitOne()方法是否該執行等待(true為收到通知,不會進入WaitOne等待)

所以_pauseEvent一開始必須是true;而_shutdownEvent則是false且WaitOne內為0

而WaitOne()本身會回傳一boolean值表示WaitHandle的通知狀態

故停止事件可以寫作

 if (_shutdownEvent.WaitOne(0)) break;

另外我在此的工作內容為印出現在時間

並用SpinWait.SpinUntil(() => false, 1000); 讓執行緒休息一秒

如果想簡略,也可把這一秒加在停止事件中,變成

if (_shutdownEvent.WaitOne(1000)) break;

只是這樣以後可能會發生不知道自己在寫甚麼(汗)

接著就是用Console來測試看看囉!

class Program
    {
        static void Main(string[] args)
        {
            ThreadWorker _threadWorker = new ThreadWorker();

            Console.WriteLine("[Start 5s]");
            _threadWorker.Start();
            Thread.Sleep(5000);

            Console.WriteLine("[Pause 10s]");
            _threadWorker.Pause();
            Thread.Sleep(10000);

            Console.WriteLine("[Resume 3s]");
            _threadWorker.Resume();
            Thread.Sleep(3000);

            Console.WriteLine("[Stop]");
            _threadWorker.Stop();
            Thread.Sleep(5000);

            Console.WriteLine("[Start again 3s]");
            _threadWorker.Start();
            Thread.Sleep(3000);

            Console.WriteLine("[Pause 3s]");
            _threadWorker.Pause();
            Thread.Sleep(3000);

            Console.WriteLine("[Resume 2s]");
            _threadWorker.Resume();
            Thread.Sleep(2000);

            Console.WriteLine("[Stop]");
            _threadWorker.Stop();

            Console.ReadKey();
        }
    }

印出的結果為

你可能會想問執行5秒怎會印出6個秒數

原因是你呼叫暫停或停止後

當下迴圈還是必須要跑完才會看到停止/暫停

所以會多做一次工才結束/停止

 

新手發文,有謬誤請告知,也請多多指教。