2015-07-11 33 views
0

的UI可以使用IProgressBackgroundWorker.ReportProgress傳遞從異步任務的信息:Control.Invoke()與IProgress/ReportProgress

class Approach1 : UserControl 
{ 
    enum Stage { INIT, STATUS, DATA, TIME, ... } 
    struct ProgressObject 
    { 
     int StatusCode; 
     int SecondsRemaining; 
     IList<int> Data; 
     Stage CurrentStage; 
    } 

    TextBox Status; 

    async Task DoWork1(IProgress<ProgressObject> progress) 
    { 
     await Task.Run(() => 
     { 
      progress.Report(new ProgressObject(0, 0, null, Stage.INIT)); 
      int code = DoSomething(); 
      progress.Report(new ProgressObject(code, 0, null, Stage.STATUS)); 
      IList<int> Data = ...; 
      progress.Report(new ProgressObject(0, 0, Data, Stage.DATA)); 
      int Seconds = ...; 
      progress.Report(new ProgressObject(0, time, null, Stage.TIME)); 
     }); 
    } 

    void ReportProgress(ProgressObject progress) 
    { 
     switch (progress.CurrentStage) 
     { 
      case Stage.CODE: 
       Status.Text = DecodeStatus(progress.StatusCode); 
       break; 
      // And so forth... 
     } 
    } 

    async void Caller1(object sender, EventArgs e) 
    { 
     var progress = new Progress<ProgressObject>(ReportProgress); 
     await DoWork2(progress); 
    } 
} 

然而,這也可以通過使一個委託的UI對象的BeginInvoke完成方法(如果我們想阻止Invoke):

class Approach2 : UserControl 
{ 
    Textbox Status; 

    int StatusCode 
    { 
     set 
     { 
      BeginInvoke(new Action(() => Status.Text = DecodeStatus(value)); 
     } 
    } 

    // Imagine several other properties/methods like the above: 
    int SecondsRemaining; 
    void UpdateData(IList<int> data); 

    async Task DoWork2() 
    { 
     await Task.Run(() => 
     { 
      StatusCode = DoSomething(); 
      UpdateData(...); 
      SecondsRemaining = ...; 
     }); 
    } 

    async void Caller2(object sender, EventArgs e) 
    { 
     await DoWork1(); 
    } 
}   

應的專用進展報告機制優於Invoke?如果,那爲什麼?兩種方法都有可能出現「陷阱」嗎?

恕我直言,在Invoke方式相比,比方說簡單/需要更少的代碼,ReportProgress接受進步結構與幾個領域,特別是如果進展是在任務和報告方法因此需要跳轉到的多個階段報告針對特定階段的適當報告。

+0

「問題代碼」與BeginInvoke vs Task.Run無關,因爲它們都服務於相同的目的。你可能寫了:'BeginInvoke(new Action(()=> progress.Report(value));'得到一個更公平的比較 - 在​​第一個例子中'做的事'在其他地方完成 – user2864740

+0

我想你誤會了 - 問題是關於使用'Invoke'與框架的專用進度報告功能。在一個情況下,調用上下文使用'Invoke'進行訪問,另一個則由'IProgress'實現隱式捕獲,兩個示例都使用'Task .Run' –

+0

請使代碼保持一致,除非它應該有所不同。因爲任務可以在沒有ReportProgress/IProgress(「更多代碼」)的情況下完成,或者可以使用它完成Invoke。 – user2864740

回答

2

你應該從你的努力中得到一個提示,使Approach2實際編譯。花了一段時間,不是嗎?我看到你反覆編輯該片段。你得到的另一個提示是唯一的方法是從UserControl派生你的類。

這是Begin/Invoke()的問題,它只有在有權訪問Control對象時才能工作。圖書館內部的代碼經常(也應該)不知道UI的外觀。它甚至可能不會在Winforms中實現,可以用於WPF或通用應用程序中。

進度<>也與這些GUI類庫一起工作,它使用更通用的方式來正確同步。由SynchronizationContext.Current屬性提供,它依賴於GUI類庫來安裝提供程序。 WinForms安裝的窗口WindowsFormsSynchronizationContext會在其Post()方法中自動調用BeginInvoke(),並在其Send()方法中自動調用Invoke()。也是使異步/等待代碼獨立於UI實現的機制。

進展<>有一個缺點,它可能完全無法以非常難以診斷的方式完成工作。對象必須由在應用程序的UI線程上運行的代碼創建。如果不是,則SynchronizationContext.Current沒有值,並且ProgressChanged事件將在任意線程池線程上觸發。 Kaboom如果嘗試用事件處理程序更新UI,則不知道爲什麼,因爲異常發生在遠離錯誤的代碼中。

但是的確,如果你將你的類硬編碼爲從System.Windows.Forms.UserControl派生,那麼你對進度<>沒什麼用處。除了感覺良好的感覺,當您將它移植到另一個GUI類庫時,您的工作量會減少。

+0

感謝您的澄清!在我真正的代碼中,我已經使用UserControl;該模式首先嚐試。聽起來像第一種模式具有的主要優點是,如果將工作方法移入庫並調用者實現其自己的進度方法,則無需更改任何內容。 –