[英]async WPF multiple status updates on UI thread
我希望通過長期運行的方法來更新狀態。 通常,我會使用分派器將其發布回UI線程,但是我對使用異步等待感到好奇。
為簡單起見:
創建一個窗口,添加一個按鈕
<Button Name="ButtonWithCodeBehind" Height="25" Click="ButtonWithCodeBehindOnClick"/>
在onClick處理程序后面添加一些代碼
private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
await Task.Factory.StartNew(() =>
{
ButtonWithCodeBehind.Content = "First";
Thread.Sleep(1000);
ButtonWithCodeBehind.Content = "Second";
Thread.Sleep(1000);
ButtonWithCodeBehind.Content = "Third";
});
}
這顯然會中斷,因為ButtonWithCodeBehind.Content將在錯誤的線程上訪問。
有沒有一種方法可以使這項工作不做類似的事情:
Deployment.Current.Dispatcher.BeginInvoke(()=>ButtonWithCodeBehind.Content = "Second");
關鍵是長時間運行的任務將隨着進度的進行生成更新,我可以將代碼重構為如下所示:
private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(() =>
{
Task.Factory.StartNew(() => Thread.Sleep(1000))
.ContinueWith(t => ButtonWithCodeBehind.Content = "First", scheduler)
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => ButtonWithCodeBehind.Content = "Second", scheduler)
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => ButtonWithCodeBehind.Content = "Third", scheduler);
});
}
但這很丑。 另外,如果您刪除了async和await關鍵字並將其替換為Task.WaitAll,它仍將按預期執行。
注意:如果您想知道為什么我使用Thread.Sleep而不是Task.Delay,我實際上也在Silverlight中對此進行了測試,並且異步等待支持不包括.Delay(或者至少不在我期望的位置)它是)。
如果您可以將長時間運行的任務拆分為兩個不同的長時間運行的操作(例如上述示例中的兩個Thread.Sleeps),則可以等待每個長時間運行的任務。 因此,UI更新將在UI線程上執行。
private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
ButtonWithCodeBehind.Content = "First";
await Task.Run(() => Thread.Sleep(1000));
ButtonWithCodeBehind.Content = "Second";
await Task.Run(() => Thread.Sleep(1000));
ButtonWithCodeBehind.Content = "Third";
}
唯一需要等待的部分是長時間運行的部分-IO調用,或者在這種情況下,是CPU受限的睡眠。
private async void ButtonWithCodeBehindOnClick(object sender, RoutedEventArgs e)
{
ButtonWithCodeBehind.Content = "First";
await Task.Factory.StartNew(() => Thread.Sleep());
ButtonWithCodeBehind.Content = "Second";
await Task.Factory.StartNew(() => Thread.Sleep());
ButtonWithCodeBehind.Content = "Third";
}
Await捕獲同步上下文,並確保將方法的其余部分簽署為延續,該延續在具有相同上下文的線程上運行。 在WPF中,UI線程處理代碼ButtonWithCodeBehindOnClick
,因此,默認情況下,它將在await
之后負責其余的方法調用。
您可以通過在任務上配置等待來覆蓋此默認行為:
await Task.Factory.StartNew(() =>
Thread.Sleep()).ConfigureAwait(continueOnCapturedContext: false);
但是,您絕對不希望在WPF中執行此操作,因為線程池線程將嘗試更新您的UI。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.