2012-12-06 20 views
8

我有這個簡單的代碼:任務<T>和TaskContinuationOptions在C#中的澄清?

var g= Task.Factory.StartNew<int> (() => 8) 
     .ContinueWith (ant =>{throw null;}) 
     .ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted); 

try{ 
     Console.WriteLine("1"); 
     g.Wait(); 
     Console.WriteLine("2"); 
    } 

catch (AggregateException ex) 
     {Console.WriteLine("catch"); } 

輸出:

1 
catch 
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. 

MSDN:

TaskContinuationOptions.NotOnFaulted

指定後續任務不應該是預定如果它的 先行拋出未處理的異常。該選項對於 多任務延續無效。

好的。

enter image description here

而且它是確定的 - 不顯示此行引起上一頁線DID拋出異常。

問題:

  • 我會得到AggregateException異常,因爲我還沒有檢查了Exception財產?

  • 必須我總是檢查前提是否拋出異常(在每一行?)? (我無法檢查每一行!它沒有任何意義和非常討厭

  • 不是try catch塊應該吞下例外呢? (我認爲所有的異常泡到wait方法....這樣嗎?)

+0

_is,如此罕見的話題_ –

+0

唉,這個話題是不是如此罕見。我在自定義的'TaskEx'類中使用了幾種擴展方法來處理它。 –

+0

什麼是唉? –

回答

5

我會得到AggregateException異常,因爲我還沒有檢查 的Exception財產?

不,你會得到一個例外,因爲任務g由TPL取消(因爲,正如MSDN指出,這個任務就不會,如果antescendent任務拋出一個異常預定)。

我們有3個任務在這裏:

  1. 原始任務(即使用StartNew)
  2. 第一凸起的任務(即拋出異常)
  3. 第二後續任務(即打印OK)(此爲g來自您的代碼的任務)。

問題是,如果第二項任務將成功完成,您要求TPL啓動3d任務。這意味着如果這個條件不符合TPL將完全取消你新創建的任務。

你有沒有觀察到任務異常,因爲你有臨時任務(任務2在我的列表),你從來沒有觀察到。因爲你從來沒有觀察到它的故障狀態,它會拋出終結器來告訴你。

您可以通過打印在catch塊任務的狀態檢查:

catch (AggregateException ex) 
{ 
    Console.WriteLine("catch"); 
    // Will print: Status in catch: Canceled 
    Console.WriteLine("Status in catch: {0}", g.Status); 
} 

必須的,如果前因拋出一個例外,我總是檢查(每 行?)? (我不能檢查各行它沒有任何意義和 很煩人!)

是的,你應該遵守先行任務例外,以避免此問題:

static class TaskEx 
{ 
    public static Task ObserverExceptions(this Task task) 
    { 
     task.ContinueWith(t => { var ignore = t.Exception; }, 
          TaskContinuationOptions.OnlyOnFaulted); 
     return task; 
    } 
} 

,然後用它如下:

var g= Task.Factory.StartNew<int> (() => 8) 
     .ContinueWith (ant =>{throw null;}) 
     .ObserveExceptions() 
     .ContinueWith (a =>{ Console.WriteLine("OK");}); 

try{ 
     Console.WriteLine("1"); 
     g.Wait(); 
     Console.WriteLine("2"); 
    } 

catch (AggregateException ex) 
     {Console.WriteLine("catch"); } 

UPDATE:加溶液最後一顆子彈

是不是try catch塊應該吞下異常? (我 認爲所有的異常泡到wait方法....這樣嗎?)

我們已成立的擴展方法在我們的項目(稱爲TransformWith)的,可以解決這一具體問題和增益如下:

  1. 異常將向上冒泡到catch塊和
  2. 我們將不會與TaskUnobservedException

這裏的美國崩潰的應用程序GE

var g = Task.Factory.StartNew(() => 8) 
     .ContinueWith(ant => { throw null; }) 
     // Using our extension method instead of simple ContinueWith 
     .TransformWith(t => Console.WriteLine("OK")); 

try 
{ 
    Console.WriteLine("1"); 
    // Will fail with NullReferenceException (inside AggregateExcpetion) 
    g.Wait(); 
    Console.WriteLine("2"); 
} 

catch (AggregateException ex) 
{ 
    // ex.InnerException is a NullReferenceException 
    Console.WriteLine(ex.InnerException); 
} 

這裏是一個擴展方法:

static class TaskEx 
{ 
    public static Task TransformWith(this Task future, Action<Task> continuation) 
    { 
     var tcs = new TaskCompletionSource<object>(); 
     future 
      .ContinueWith(t => 
      { 
       if (t.IsCanceled) 
       { 
        tcs.SetCanceled(); 
       } 
       else if (t.IsFaulted) 
       { 
        tcs.SetException(t.Exception.InnerExceptions); 
       } 
       else 
       { 
        try 
        { 
         continuation(future); 
         tcs.SetResult(null); 
        } 
        catch (Exception e) 
        { 
         tcs.SetException(e); 
        } 
       } 
      }, TaskContinuationOptions.ExecuteSynchronously); 

     return tcs.Task; 
    }  
} 
+0

謝爾蓋,關於你的最後一個代碼:如果我使用'OnlyOnFaulted'--我的應用程序不會死(右?)導致'exception'屬性_has_被檢查。和SO,我可以做'拋出myNewException'這將被緩存在'catch'子句中(而且我的應用程序不會死)** right **? –

+0

如果您在上一次ContinueWith中添加OnlyOnFaulted,您仍然會收到AggregateException,因爲無論如何g會將其狀態更改爲已取消。 –

+0

@RoyiNamir:我改變了澄清:'你從未觀察過的另一個問題是你有臨時任務(我列表中的任務2)。因爲你從來沒有觀察到它的故障狀態,它會拋出終結器來告訴你。' –

3
  • 我會得到AggregateException異常,因爲我還沒有檢查 Exception屬性?

任務總是拋出AggregateException:http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx

可以使用得到原始異常:

var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); }); 
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException; 
  • 必須的,如果前因拋出一個例外,我總是檢查(在每個 線?)? (我無法檢查每一行!它沒有任何意義和 很煩人)

是的,它是煩人,你應該創建兩個延續爲每一個任務檢查例外:一個是檢查是否出現了例外處理它,如果沒有例外,另一個繼續操作見TaskContinuationOptions.OnlyOnFaultedTaskContinuationOptions.OnlyOnRanToCompletion。 如果需要,您甚至應該創建第三個延續來處理取消。

  • 不是try catch塊應該吞下例外呢? (我 認爲所有異常冒泡到等待方法....所以?)

不,它不會,異常不會在更高的級別拋出,你應該使用 TaskContinuationOptions.OnlyOnFaulted在任務延續,以檢查是否有是個例外。您可以在來電者的水平得到任務異常只能用 async關鍵字在.NET中不可用4個

+0

Alex你用了3次'async'這個詞,我的問題被標記爲fw4。我無法做任何事情...... –

+0

你說得對,我已經更新了回覆。有關信息,您可以使用.net 4的異步:http://nuget.org/packages/Microsoft.Bcl.Async(但只在VS 2012中) – AlexH

+0

因此,即使我使用TaskContinuationOptions.OnlyOnFaulted,我仍然必須吞下/用'var dummy = a.Exception;'來檢查它吧? (http://i.stack.imgur.com/s8j9n.jpg) –

0

手柄AggregateExceptions是這樣的:

catch(AggregateException aex) 
{ 
    aex.Handle(ex => 
    { 
     // Do some handling and logging 
     return true; 
    } 
}