2015-02-10 59 views
12

此代碼片段來自Stephen Cleary's blog,並給出了使用Task.Run時如何報告進度的示例。我想知道爲什麼在更新UI時沒有交叉線程問題,我的意思是爲什麼不需要調用?Task.Run和UI進度更新

private async void button2_Click(object sender, EventArgs e) 
{ 
    var progressHandler = new Progress<string>(value => 
    { 
     label2.Text = value; 
    }); 
    var progress = progressHandler as IProgress<string>; 
    await Task.Run(() => 
    { 
     for (int i = 0; i != 100; ++i) 
     { 
      if (progress != null) 
       progress.Report("Stage " + i); 
      Thread.Sleep(100); 
     } 
    }); 
    label2.Text = "Completed."; 
} 
+0

@newbieguy字符串連接自動調用ToString()方法 – 2018-03-01 07:11:27

回答

19

Progress<T>當它被實例化捕捉當前SynchronisationContext。無論何時您致電Report,它都會將其祕密委託給捕獲的上下文。在該示例中,捕獲的上下文是UI,這意味着不會發生任何異常。

+0

你知道給線程優先級嗎?即:正常,背景,ApplicationIdle等。 – Kelly 2017-05-17 02:05:21

+0

@Kelly調度程序優先級爲正常。請參閱此處的備註部分https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchersynchronizationcontext.post(v=vs.110).aspx – Gusdor 2017-05-17 05:46:02

8

構造函數Progress<T>捕獲當前SynchronizationContext對象。

SynchronizationContext類是抽象涉及的線程模型的細節的工具。也就是說,Windows窗體將使用Control.Invoke,在WPF將使用Dispatcher.Invoke

progress.Report對象被調用時,Progress對象本身知道它應該使用捕獲SynchronizationContext運行其委託。

換句話說,它的工作原理是因爲Progress被設計來處理,而開發人員不必明確地說出來。

4

看來你是因爲事實證明這跨線程機制的一部分是從開發商的眼睛隱藏的,所以你只需要「採取和使用」困惑:http://blogs.msdn.com/b/dotnet/archive/2012/06/06/async-in-4-5-enabling-progress-and-cancellation-in-async-apis.aspx

我們推出了IProgress接口使您能夠創建顯示進度的體驗 。該界面公開了一個Report(T) 方法,異步任務調用該方法報告進度。您在異步方法的簽名中公開此接口,並且調用者必須提供實現此接口的對象。在一起,任務 和調用者創建了一個非常有用的鏈接(並且可以在不同的線程上運行 )。

我們還提供了Progress類,它是 IProgress的實現。我們鼓勵您在您的 實施中使用Progress,因爲它可以處理所有關於保存 的簿記並恢復同步上下文。進度顯示 事件和一個動作回調,當任務 報告進度時會調用這個回調。這種模式使您能夠編寫簡單的代碼,以便在發生更改時對其進行更改。 IProgress和 Progress一起提供了一種將 後臺任務的進度信息傳遞給UI線程的簡單方法。

還有一兩件事提:進度通知將後調用作業的部分已經完成,不只是在那一刻。所以,如果你的UI線程空閒並且你有空閒的CPU內核,延遲幾乎爲零。如果您的UI線程繁忙,則在UI線程回到空閒狀態(不管您的計算機有多少備用CPU核心)之前,不會調用該通知。