2012-06-21 78 views
2

我在WinForms中使用任務從我的UI線程中刪除昂貴的方法。在我的updateComplete和updateFailed任務中,我必須將_updateMessageTaskInProgress設置爲false並啓用我的控件。有沒有什麼辦法可以在一個單獨的任務中完成這個任務,一旦完成任務,updateComplete或updateFailed就會繼續執行(因爲我目前有重複的代碼)?另外,有沒有更好的實現_updateMessageTaskInProgress的方法 - 我不想同時運行多個任務。任務延續以保持UI線程響應

private void PerformUpdate() 
{ 
    if (!_updateMessageTaskInProgress) 
     { 
      LoadButton.Enabled = false; 
      MonthEndDateEdit.Enabled = false; 
      BankIssuerListEdit.Enabled = false; 

      Task updateMessages = Task.Factory.StartNew(() => 
      { 
       _updateMessageTaskInProgress = true; 

       ExpensiveMethod(); 
      }); 

      // Task runs when updateMessages completes without exception. Runs on UI thread. 
      Task updateComplete = updateMessages.ContinueWith(update => 
      { 
       DoSuccessfulStuff(); 

       _updateMessageTaskInProgress = false; 
       LoadButton.Enabled = true; 
       MonthEndDateEdit.Enabled = true; 
       BankIssuerListEdit.Enabled = true; 
      }, System.Threading.CancellationToken.None, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); 

      // Task runs when updateMessages completes with exception. Runs on UI thread. 
      Task updateFailed = updateMessages.ContinueWith(task => 
      { 
       DoFailureStuff(); 

       _updateMessageTaskInProgress = false; 
       LoadButton.Enabled = true; 
       MonthEndDateEdit.Enabled = true; 
       BankIssuerListEdit.Enabled = true; 
      }, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); 
     } 
} 
+0

你的'_updateMessageTaskInProgress'有一個競爭條件:如果兩個任務同時啓動,那麼有可能兩個任務同時運行。 – svick

回答

1

我會用這個Event Based Asynchronous-TYPE Pattern。我用分拆法到使用TPL後臺線程的代碼的簡化版本低於

private void TaskSpin(TaskScheduler uiScheduler, 
         Func<TaskScheduler, object[], bool> asyncMethod, 
         object[] methodParameters) 
{ 
    try 
    { 
     Task asyncTask = Task.Factory.StartNew<bool>(() => 
      asyncMethod(uiScheduler, methodParameters)); 

     // Callback for finish/cancellation. 
     asyncTask.ContinueWith(task => 
     { 
      // Check task status. 
      switch (task.Status) 
      { 
       // Handle any exceptions to prevent UnobservedTaskException.    
       case TaskStatus.RanToCompletion: 
        if (asyncTask.Result) 
         UpdateUI(uiScheduler, "OK"); 
        else 
        { 
         string strErrComplete = "Process failed."; 
         UpdateUI(uiScheduler, strErrComplete); 
        } 
        break; 
       case TaskStatus.Faulted: 
        string strFatalErr = String.Empty; 
        UpdateUI(uiScheduler, "Fatal Error); 
        if (task.Exception != null) 
         strFatalErr = task.Exception.InnerException.Message; 
        else 
         strFatalErr = "Operation failed"; 
        MessageBox.Show(strFatalErr); 
        break; 
      } 
      asyncTask.Dispose(); 
      return; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
    catch (Exception eX) 
    { 
     Utils.ErrMsg(eX.Message); 
    } 
} 

我希望這有助於。

編輯。請注意,在上面的uiScheduler是UI線程的TaskScheduler。也就是說

TaskSheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
+1

如此多的問題:這個EAP如何? (我在代碼中沒有看到任何事件。)將'asyncTask'作爲一個從未使用過的參數有什麼意義?爲什麼'asyncMethod'將'TaskScheduler'作爲參數? – svick

+0

這是EAP - 這裏的'task.ContinueWith'是我們的活動。繼續執行通常由AsyncCallback委託執行的工作。當前綴完成時調用它,並且數據緩衝區已填充。 'asyncTask'被使用,它被傳遞到我的目的的方法(我在問題中聲明,這是我有一些代碼的修剪版本 - 也許我應該做一些更多的修剪:])。最後,'uiScheduler'被傳入該方法,以便在該方法中可以更新UI線程。 – MoonKnight

+0

我編輯了這個問題來刪除'Task'參數。 – MoonKnight

2

你爲什麼不只是提取方法?

private void SetLock(bool lock) 
    { 
     LoadButton.Enabled = !lock; 
     MonthEndDateEdit.Enabled = !lock; 
     BankIssuerListEdit.Enabled = !lock; 
     _updateMessageTaskInProgress = lock; 
    } 

    private void PerformUpdate() 
    { 
     if (!_updateMessageTaskInProgress) 
     { 
      SetLock(true); 
      Task updateMessages = Task.Factory.StartNew(() => 
      { 
       ExpensiveMethod(); 
      }); 

      // Task runs when updateMessages completes without exception. Runs on UI thread. 
      Task updateComplete = updateMessages.ContinueWith(update => 
      { 
       DoSuccessfulStuff(); 
       SetLock(false); 
      }, System.Threading.CancellationToken.None, TaskContinuationOptions.NotOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); 

      // Task runs when updateMessages completes with exception. Runs on UI thread. 
      Task updateFailed = updateMessages.ContinueWith(task => 
      { 
       DoFailureStuff(); 
       SetLock(false); 
      }, System.Threading.CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); 
     } 
    } 
+1

您必須在失敗的處理程序中訪問task.Exception,以避免任務終結器出現異常 – adrianm