顯示具有 Thread 標籤的文章。 顯示所有文章
顯示具有 Thread 標籤的文章。 顯示所有文章

2018年12月11日 星期二

[Qt]分離Ui執行緒與Worker執行緒


有時候為了增進Ui的響應速度,不得不把例行工作,尤其那些需要定時呼叫或狀態控制的工作,移到背景執行緒,形成雙執行緒的工作架構(基本上就是自動化產業的101招):

  • 主執行緒(Main thread)負責Ui的訊息迴圈,捕捉使用者事件如滑鼠點擊,鍵盤按下等等
    • 通常開發框架都幫忙搞定好訊息迴圈架構了,我們只需要填寫各事件的處理常式
      • 心得:處理一圈迴圈的延遲能在100ms內搞定,使用者基本上會感覺流暢

  • 背景執行緒(Working thread),可能就負責收集外部I/O的資訊,內部物件狀態控制,等等...與介面無關的工作
    • 時脈上就與使用者的反應無關,這個執行緒Looping的速度盡量越快越好
      • 心得:處理一圈的延遲能在10ms內搞定,順序控制在大部分應用情況不會太糟

2017年7月20日 星期四

[.NET] 執行序同步慣用Pattern心得 - I


MSDN說:
需要使用者互動的軟體必須儘快回應使用者的活動,以便提供豐富的使用者經驗。不過,同時它也必須執行需要的計算以儘快提供使用者資料。
在個人目前的工作領域,使用者體驗並非放在第一(甚至是最後一名),大部分時間都花在Coding工作處理常式(Routines),主要實現概念為狀態機(State-Machine)與多工處理(Multi-Task),在Main Thread執行

常式內的多工工作是純粹的商業邏輯,少有牽涉I/O存取,處理頻率穩定近乎週期性,但來源資訊難免要透過外部裝置取得,如網路介面、PCI裝置、使用者輸入...等,幾乎是慢速、頻率較低的資料源,為了避免影響Main Thread的多工輪詢,此時就可考慮用其他Thread分擔低速工作。

整理三個與狀態機搭配的常用分緒招,共通點是在Main Thread輪詢某資料狀態,該輪詢函式必須不能阻塞(Blocking)住Main Thread,否則會影響到其他多工作業

(沒實測過這些方法的具體效能,比較在乎應用上的設計模式(Pattern)是否容易嵌入既有架構)
  • Task
    • 發動:
      • 將要執行的Method裝載到Task物件中後,呼喚Start()開始執行緒作業
      • 也可以利用工廠模式,呼叫共用方法Task.Run()開始執行緒作業,同步狀態透過傳回的Task實例來操作
    • 同步:
      • Worker Thread輪詢Task.IsComplete至狀態為True
    • 心得:
      • Task封裝了Thread與若干執行緒同步元件(如Mutex),因此包含了執行緒操作與同步操作
      • 據說是在執行緒池內作業(待確認
  • ThreadPool
    • 發動:
    • 同步:
      • 無法得知方法最終被放到哪個具體執行緒,也沒封裝同步操作方法,所以必須在Method裡自行設計同步物件來辨識是否方法執行完成
      • 個人慣用Concurrent執行緒安全元件,如ConcurrentQueueConcurrentDictionary傳遞資訊到Worker Thread
      • Worker Thread輪詢Concurrent元件
    • 心得:
      • 最簡單易用赤裸的異步編成模式,不含任何同步方法的純粹執行緒池操作最?似乎懂了些什麼了?
  • MulticastDelegate
    • 發動:
      • 將要執行的Method裝載到Delegate中
      • 呼叫該Delegate的BeginInvoke(),必須帶入回呼方法(Callback)
      • 必須呼叫EndInvoke()作為確認該執行緒執行完成的辨識?
    • 同步:
      • 輪詢呼叫該Delegate時傳回的IAsyncResult.IsCompleted
      • 或是在Main Thread呼叫EndInvoke()(但是可能阻塞)
    • 心得:
      • 與Task一樣,封裝了執行緒操作與同步操作,只是設計模式上透過IAsyncResult或EndInvoke達成同步
      • 據說是在執行緒池內作業(待確認
class Program
    {


        static void Main(string[] args)
        {
            int state = 0;

            Action __delegate = null; 
            Task __task = null;
            IAsyncResult handle = null;
            ConcurrentQueue exchange = new ConcurrentQueue();

            int outValue = 0;
            //the main state machine
            Program.method();
            while (state != 500)
            {
                switch (state)
                {
                    case 0:
                        // use Task 
                        __task = new Task(new Action(Program.method));
                        __task.Start();
                        state +=10;
                        break;
                    case 10:
                        // wait until task done
                        if (__task.IsCompleted)
                            state +=10;
                        break;
                    case 20:
                        // use thread pool
                        ThreadPool.QueueUserWorkItem((object foo)=>{
                            Program.method();
                            exchange.Enqueue(0); // enqueue some meaningfll result
                        },null);
                        state+=10;
                        break;
                    case 30:
                        //wait until method had been scheduled and executed
                        if (exchange.TryDequeue(out outValue))
                            state +=10;
                        break;
                    case 40:
                        // use delegate/IAsyncResult 
                        __delegate =  new Action(method);
                        handle = __delegate.BeginInvoke((IAsyncResult ar)=> 
                            {
                                method();
                                __delegate.EndInvoke(ar); // you had to call this to release ar
                            },null);
                        break;
                    case 50:
                        // wait until method had been executed
                        if (handle.IsCompleted)
                            state = 500;
                        break;
                    default:
                        break;
                }
                Thread.Sleep(0);// yield to other thread to run
            }//while
        }

        static void method()
        {
            //reflect the current thread
            Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
        }

    }

後記1:MSDN的異步編程模式概述,對於非同步作業可有概略且完整的認識,其中APM(
Asynchronous Programming Model,就是Delegate/IAyncResult)已經漸漸式微變成不太推薦的Pattern

後記2:在.NET Core(MAC)上實測Delegate.BeginInvoke是會有例外的,原因在此,大致上是被開發小組視為Deprecated方法,未來可能會被幹掉