簡體   English   中英

Dispatcher 操作的執行順序是什么(Invoke/BeginInvoke)

[英]What is the order of execution for Dispatcher operations(Invoke/BeginInvoke)

這是WPF中的測試代碼:

            Action ac0 = delegate
            {
                Console.WriteLine("action-beginInvoke-enter");
                Application.Current.Dispatcher.BeginInvoke(new Action(delegate
                {
                    Console.WriteLine("dispatcher begin invoke code");
                }));
                Console.WriteLine("action-beginInvoke-exit");
            };
            ac0.BeginInvoke(null, null);
            Console.WriteLine("ui thread sleep before");
            Thread.Sleep(1000);// ensure the ac0 is done
            Console.WriteLine("ui thread sleep after");
            Application.Current.Dispatcher.Invoke(new Action(delegate
            {
                Console.WriteLine("dispatcher invoke code");
            }));
            Console.WriteLine("ui thread exit");

Output:
ui線程睡眠之前
action-beginInvoke-enter
動作開始調用退出
ui線程睡眠后
調度程序調用代碼
ui線程退出
調度程序開始調用代碼

然后,我將 Invoke 代碼刪除到另一個項目中並編譯為 a.dll:

            Action ac0 = delegate
            {
                Console.WriteLine("action-beginInvoke-enter");
                Application.Current.Dispatcher.BeginInvoke(new Action(delegate
                {
                    Console.WriteLine("dispatcher begin invoke code");
                }));
                Console.WriteLine("action-beginInvoke-exit");
            };
            ac0.BeginInvoke(null, null);
            Console.WriteLine("ui thread sleep before");
            Thread.Sleep(1000);// ensure the ac0 is done
            Console.WriteLine("ui thread sleep after");
            Test.WriteLine();// Test is a static class from dll
            Console.WriteLine("ui thread exit");

這是 Test.WriteLine:

        Application.Current.Dispatcher.Invoke(new Action(delegate
        {
            Console.WriteLine("dispatcher invoke code");
        }));

output 是:
ui線程睡眠之前
action-beginInvoke-enter
動作開始調用退出
ui線程睡眠后
調度程序開始調用代碼
調度程序調用代碼
ui線程退出

我試圖弄清楚 Dispatcher 操作的執行順序。 據我所知,UI 線程一直很忙,直到執行到最后一行。 在此之前它如何執行代碼“調度程序開始調用代碼”? 並且代碼是相同的,除了 Dispatcher.Invoke 被刪除到 dll 中。 為什么他們的 output 不一樣?

對您的問題的簡短回答 - 執行順序是什么 - 是,您擁有它的方式是不可預測的。 但是我們可以通過使用TaskCompletionSource使其可預測。

長答案 - 你在這里發生了一些不同的事情,我認為這是你意想不到的結果的根源。

首先,您在ac0上使用Action.BeginInvoke 這異步調用ac0 ,即在后台,可能(如果不總是?)在與調用BeginInvoke的線程不同的線程上。 除非您采取特定步驟(我將在下面概述),否則委托與您的其他代碼的執行順序是未定義的,並且可能看起來是隨機的。

其次,在委托ac0 (它再次異步運行)內部,您將使用另一個委托調用Dispatcher.Invoke 我們稱之為ac0_1 ac0_1是實際寫入控制台的內容。 Dispatcher.InvokeAction.BeginInvoke的不同之處在於,您提供給Dispatcher.Invoke的任何內容始終在可預測的線程(UI 線程)上執行。 (使用Action.BeginInvoke您不知道它將在哪個線程上執行,除非它(當然?)不會是 UI 線程)。 Dispatcher.Invoke的不同之處還在於它在委托完成之前不會返回 - 這意味着它會阻塞調用線程 - 這使得與異步操作混合非常危險。

因此,通過混合Dispatcher.InvokeAction.BeginInvoke ,您混合了蘋果和橙子,並且很少會得到可預測的結果。 因此,除非您要處理無法管理的回調鏈和/或總應用程序鎖定的高可能性,否則 IMO 保證代碼按您期望的順序執行的最佳方法是使用TaskCompletionSource 它會像這樣工作:

        TaskCompletionSource<object> task1 = new TaskCompletionSource<object>();        
        Action ac0 = delegate
        {
           Application.Current.Dispatcher.InvokeAsync(new Action(delegate
           {
              Console.WriteLine("ac0");
              task1.SetResult(null);
           }));
        };
        ac0.BeginInvoke(null, null);
        await task1.Task; // ensure the ac0 is done

        TaskCompletionSource<object> task2 = new TaskCompletionSource<object>();
        Application.Current.Dispatcher.InvokeAsync(new Action(delegate
        {
           Console.WriteLine("ac1");
           task2.SetResult(null);
        }));
        await task2.Task; 
        Console.WriteLine("over");

幕后發生的事情是await task1.Task之后的所有內容本質上都變成了在調用task1.SetResult(null)時執行的回調,但它仍然允許您編寫具有明顯線性執行流程的代碼。

您還會注意到我將Dispatcher.Invoke更改為Dispatcher.InvokeAsync 為什么? 避免鎖定的可能性。 當您第一次調用Dispatcher.Invoke時,我們不知道哪個線程實際上處於活動狀態,但無論它是什么都將被阻塞,直到委托完成。 由於我們實際上從不想阻塞線程(因此實際上從不想使用 Thread.Sleep),所以最好始終使用這些方法的異步版本並使用TaskCompletionSource來獲取等待的Task ,以確保項目在繼續之前已完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM