我有这段代码:
public async Task AsyncMethod()
{
await Task.Factory.StartNew(() =>
{
throw new Exception();
});
}
public ActionResult Index()
{
var t1 = Task.Factory.StartNew(() => { throw new Exception(); });
var t2 = Task.Factory.StartNew(() => { throw new Exception();});
var t3 = Task.Factory.StartNew(async () => { await AsyncMethod(); });
try
{
Task.WaitAll(t1, t2, t3);
}
catch (AggregateException ex)
{
var count1 = ex.InnerExceptions.Count;
var count2 = ex.Flatten().InnerExceptions.Count;
throw;
}
return View();
}
我想了解为什么count1和count2变量是2而不是3,我怎样才能在AsyncMethod中得到第三个异常?
答案 0 :(得分:3)
更改此
var t3 = Task.Factory.StartNew(async () => { await AsyncMethod(); });
到此:
var t3 = AsyncMethod();
您已经拥有任务。无需通过在线程池上执行它来包装它。
通过包装它,您将已经拥有的Task
(最终将会出错)转换为永不发生故障的Task<Task>
(仅Task.Result
个故障)。当然WaitAll
对外部任务进行操作,因此它永远不会看到异常。
使用调试器逐步执行此类代码以检查所有变量的运行时值是一种很好的技术。通过查看代码比这更容易注意到这个错误(“看起来”对我来说已经足够了,因为我对此有经验 - 但如果我没有经验,我将需要运行此代码和调试它)。
答案 1 :(得分:3)
我想了解为什么count1和count2变量是2而不是3,我怎样才能在AsyncMethod中得到第三个异常?
Task.Factory.StartNew
返回基本Task
。如果您将async
委托传递给它,则返回的Task
仅表示async
方法的开头(直到它向其调用方产生的点)。
您应该将Task.Run
与async
代码一起使用。 Task.Run
会为Task
委托创建async
包装,因此从Task
返回的Task.Run
代表整个async
方法。
Stephen Toub有an excellent blog post detailing the differences between Task.Run
and Task.Factory.StartNew
。
另外,正如我们所提到的,每当你在GUI或ASP.NET上下文中阻塞Task
而不是await
时,就会遇到死锁问题。我有a blog post that goes into detail about this deadlock problem。您应该使用await Task.WhenAll
代替Task.WaitAll
。
所以,这里是你的代码应用了两个更改:
public async Task AsyncMethod()
{
await Task.Run(() =>
{
throw new Exception();
});
}
public async Task<ActionResult> Index()
{
var t1 = Task.Run(() => { throw new Exception(); });
var t2 = Task.Run(() => { throw new Exception();});
var t3 = Task.Run(async () => { await AsyncMethod(); });
try
{
await Task.WhenAll(t1, t2, t3);
}
catch (Exception)
{
var ex1 = t1.Exception.InnerException;
var ex2 = t2.Exception.InnerException;
var ex3 = t3.Exception.InnerException;
throw;
}
return View();
}