[英]Task<T> and TaskContinuationOptions Clarification in 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
指定如果延續任務的前端拋出未處理的異常,則不應該對其進行調度。 此選項對多任務延續無效。
好 。
並且沒關系 - 不顯示此行導致prev行DID拋出異常。
問題:
我是否收到了AggregateException
異常,因為我沒有檢查過Exception
屬性?
我是否必須始終檢查先前是否會拋出異常(在每一行?)? ( 我不能檢查每一行!它沒有任何意義,非常煩人 )
是不是try catch
塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)
我是否收到了
AggregateException
異常,因為我沒有檢查過Exception
屬性?
不,你得到一個例外,因為任務g
被TPL取消(因為,如msdn所述,如果antescendent任務拋出異常,則不會安排此任務)。
我們這里有3個任務:
問題是, 只有當第二個任務成功完成時 ,才會要求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"); }
更新:為最后一顆子彈添加了解決方案
是不是try catch塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)
我們在項目中有一組擴展方法(稱為TransformWith
),可以解決這個特定問題,並獲得以下內容:
TaskUnobservedException
崩潰應用程序 這里的用法
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;
}
}
我是否收到了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.OnlyOnFaulted
和TaskContinuationOptions.OnlyOnRanToCompletion
。 如果需要,您甚至應該創建第三個延續來處理取消。
是不是try catch塊應該吞下異常? (我認為所有異常都會導致等待方法....所以?)
TaskContinuationOptions.OnlyOnFaulted
來檢查是否存在異常。
只能使用.net 4中不可用的async關鍵字,才能在調用者級別獲取任務異常
像這樣處理AggregateExceptions:
catch(AggregateException aex)
{
aex.Handle(ex =>
{
// Do some handling and logging
return true;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.