2015-10-30 13 views
0

我在任務中有一個無限循環。在某些情況下,該任務會引發異常並終止。考慮下面的代碼片段。如何從ContinueWith傳播異常到無限循環任務的調用上下文

private async void Button_Click(object sender, RoutedEventArgs e) 
    { 
     try 
     { 
      int x = await FirstTask(); 
      window.Title = "FirstTask completed with " + x.ToString(); 
     } 
     catch (ArgumentException ex) 
     { 
      textbox.Text = ex.Message; 
     } 
    } 

    public async Task<int> FirstTask() 
    { 
     Task<int> secondTask; 
     int result; 

     secondTask = SecondTask(); 
     textbox.Text = "Awaiting SecondTask result"; 
     result = await secondTask; 
     textbox.Text = result; 

     secondTask.ContinueWith(async (Task t) => 
     { 
      var thirdTask = ThirdTask(); 

      thirdTask.ContinueWith(
       async (m) => 
        await Task.Run(() => 
        { 
         throw thirdTask.Exception.InnerException; 
        }), 
       TaskContinuationOptions.OnlyOnFaulted); 

     }, TaskContinuationOptions.OnlyOnRanToCompletion); 

     return 5; 
    } 

    public async Task<int> SecondTask() 
    { 
     await Task.Delay(1500); 
     return 8; 
    } 

    public async Task ThirdTask() 
    { 
     while (true) 
     { 
      await Task.Delay(500); 

      throw new ArgumentException("thirdException"); 
     } 
    } 

我的問題在於無法將從ThirdTask拋出的異常傳播到Button_Click事件。很明顯,等待它不是一個選項,因爲它是一個持續的無限操作(這只是簡化爲快速失敗)。然而,對於等待重新拋出異常的「短」任務,如果它僅在ThirdTask失敗時才觸發,則沒有問題。請注意,我對ThirdTask的行爲沒有興趣,除非它失敗了,也就是說,我可以等待事件處理程序中的FirstTask。

實驗表明,即使是最簡單的示例也不會從ContinueWith塊傳播異常。

 private async void Button_Click(object sender, RoutedEventArgs e) 
    { 
     try 
     { 
      Task task = Task.Run(async() => { await Task.Delay(1000); }); 
      task.ContinueWith((t) => { throw new ArgumentException("test"); }, TaskContinuationOptions.OnlyOnRanToCompletion);   
     } 
     catch (ArgumentException ex) 
     { 
      textbox.Text = ex.Message; 
     } 
    } 

那麼,我該如何從傳播到ContinueWith調用上下文的異常,因爲,拋出它具有一個無限循環的任務,這使我的等待呢?

我試圖解決的問題有兩個:首先,我需要初始化一個資源(FirstTask),爲了做到這一點,我首先需要獲取它(SecondTask),然後開始一個(ThirdTask),最後,資源的初始化(FirstTask)返回一個值,表示資源的狀態,它不依賴於進程(ThirdTask)。進程(ThirdTask)重複調用另一個任務(在本例中爲Task.Delay)並對其執行一些工作,但可能會失敗。在這種情況下,它會拋出一個需要處理的異常。

第二部分是第二個代碼示例的一般情況,說明如何從ContinueWith中引發異常以便由調用上下文處理。

+2

其實,等待*爲*解決方案和無限循環不會造成任何影響。一個異常*會*中斷循環,因此等待從'ContinueWith'返回的任務將捕獲異常。事實上,你甚至不需要'ContinueWith',你可以'在適當的excetion處理塊中等待ThirdTask()'。 'await'也會解開拋出的異常,所以你會得到最初的'ArgumentException' –

+2

爲什麼複雜的代碼和'ContinueWith'?你試圖解決什麼實際問題? '等待SecondTask();等待ThirdTask();'應該足夠了,既可以在'SecondTask()'*之後啓動'ThirdTask()',也可以傳播任何異常 –

+0

@PanagiotisKanavos不會等待ThirdTask在那裏阻止調用方法ThirdTask完成(在我的情況下最終是按鈕點擊處理程序)?如果沒有拋出異常,那將是永遠的,這是不希望的。例外情況不應該經常發生,但如果發生,我應該能夠處理它。複雜的代碼被簡化以強調問題。每個任務都有所作爲,所以這只是程序結構的一個大綱。 – user3209815

回答

1

鑑於引發它的任務有無限循環,從而阻止我等待它?

這絕不會阻止您等待它。處理這種拋出異常的情況的[最簡單]方法是專門用於await它。

你可以簡單的實現方法是這樣的:

public async Task FirstTask() 
{ 
    Task<int> secondTask = SecondTask(); 
    textbox.Text = "Awaiting SecondTask result"; 
    textbox.Text = await secondTask; 
    await ThirdTask(); 
} 

如果點擊處理程序需要兩個更新與第二操作結果的texbox,如果第三失敗更新UI,那麼你需要在FirstTask不換這兩個操作,並直接從點擊處理程序調用它們:

private async void Button_Click(object sender, RoutedEventArgs e) 
{ 
    try 
    { 
     textbox.Text = "Awaiting SecondTask result"; 
     int x = await SecondTask(); 
     window.Title = "SecondTask completed with " + x.ToString(); 
     await ThirdTask(); 
    } 
    catch (ArgumentException ex) 
    { 
     textbox.Text = ex.Message; 
    } 
}