2013-10-15 34 views
0

我試圖在(相當簡單的)應用程序中實現異步等待的東西。 我的目標是在等待之間更新busyIndi​​cator。在2個等待句子之間更新UI

我不知道是什麼,但我認爲我錯過了一些基本的理解異步等待的東西。

private async void StartTest(object obj) 
{ 
    try 
    { 
     this.IsBusy = true; 
     this.BusyMessage = "Init..." 

     await Task.Delay(7000); 


     var getData1Task = this.blHandler.GetData1Async(); 
     this.BusyMessage = "Retreiving data..."; 

     this.result1 = await getDeviceInfoTask; 
     this.result2 = await this.blHandler.GetData2Async(); 

     this.BusyMessage = "Searching..."; 

     this.result3 = await this.blHandler.GetData3(); 
    } 
    finally 
    { 
     this.IsBusy = false; 
     this.BusyMessage = string.empty; 
    } 
} 

的BusyIndi​​cator控件具有IsBusyBusyMessage的結合。 執行此代碼時,我得到呈現「初始化...」的BusyIndi​​cator控件,但它永遠不會改變爲「Retreiving數據...」或「搜索...」。 更糟:在執行最後的GetData3時,ui完全凍結。

+2

這些方法是做什麼的?他們真的是異步的嗎? – SLaks

+2

在調試器凍結時暫停並查看堆棧跟蹤。 – SLaks

+0

這段代碼是在事件處理程序中調用的嗎?該處理程序是否標記爲異步? – Baldrick

回答

2

這聽起來像你實際上想要在後臺線程上執行同步方法,然後異步等待它在UI代碼中完成。

這正是Task.Run()所做的。
它需要一個委託在ThreadPool中運行,然後返回一個await已成功的任務,併爲您提供結果。

+0

Thx。我會閱讀它並明天再試一試。 @MattSmith的回答已經給了我關於異步等待東西的重要部分。 – Koen

3

最有可能的GetData1AsyncGetData2AsyncGetData3是同步的方法(即我猜,雖然他們也有一個返回Task他們完成他們所有的同步工作)。在這種情況下,await不會暫停該方法(因爲返回的Task將是一個完成的任務)。因此,該方法將作爲一種大型同步方法一直持續下去,並且UI將永遠不會有更新的機會(因爲在此期間它不會抽出任何消息)。

如果您想要更多猜測,請向我們展示這三種方法的代碼。

+0

他們確實是返回任務的同步方法。我想我已經找到了我失蹤的重要部分。我希望這些方法可以在後臺線程上運行。所以我認爲我必須在這裏使用背景工作而不是異步等待? – Koen

+1

@Koen,而不是BackgroundWorker,請參閱@SLaks關於使用Task.Run()的答案。 –

0

你可以請嘗試按Dispatcher推送消息。

App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => 
         { this.BusyMessage = "Retreiving data..."; })); 

    // Do Something 


    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => 
        { this.BusyMessage = "Filtering data..."; })); 

    // Do Something 
0

這裏是一個工作副本,我的一些假設被剔除了。希望能幫助到你。我在一個正在運行的WPF應用程序中測試它。請注意,在您發佈的內容中沒有任何防範措施,以確保這不是「雙重運行」(無論IsBusy的值是多少,我們都可以啓動,StartTest應該可以確保這一點)。

public class StackWork : ViewModelBase 
{ 
    private class MyHandler 
    { 
     private async Task<string> GetDataAsync(string result) 
     { 
      return await Task<string>.Run(() => 
       { 
        Thread.Sleep(5000); 
        return result; 
       }); 
     } 

     public async Task<string> GetData1Async() 
     { 
      return await GetDataAsync("Data1"); 
     } 

     public async Task<string> GetData2Async() 
     { 
      return await GetDataAsync("Data2"); 
     } 

     public async Task<string> GetData3() 
     { 
      return await GetDataAsync("Data3"); 
     } 
    } 

    private bool IsBusy { get; set; } 

    private string _message = ""; 
    public string BusyMessage 
    { 
     get { return _message; } 
     set { _message = value; RaisePropertyChanged("BusyMessage"); } 
    } 
    private MyHandler blHandler = new MyHandler(); 
    private Task<string> getDeviceInfoTask; 
    private string result1 { get; set; } 
    private string result2 { get; set; } 
    private string result3 { get; set; } 

    public StackWork() 
    { 
     getDeviceInfoTask = Task<string>.Run(() => 
      { 
       return ("device info"); 
      }); 
    } 

    public async void StartTest(object obj) 
    { 
     try 
     { 
      this.IsBusy = true; 
      this.BusyMessage = "Init..."; 

      await Task.Delay(7000); 

      var getData1Task = /*this*/await/*was missing*/ this.blHandler.GetData1Async(); 
      this.BusyMessage = "Retreiving data..."; 

      //assuming this was Task.Run, put that in constructor 
      this.result1 = getDeviceInfoTask/*this*/.Result/*was missing*/; 
      this.result2 = await this.blHandler.GetData2Async(); 

      this.BusyMessage = "Searching..."; 

      //This was a little confusing because the name doesn't imply it is 
      //async, but it is awaited 
      this.result3 = await this.blHandler.GetData3(); 
     } 
     finally 
     { 
      this.IsBusy = false; 
      this.BusyMessage = string.Empty; 
     } 
    } 
}