2

我有这个代码:

    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 中获得第三个异常?

4

2 回答 2

3

改变这个

    var t3 = Task.Factory.StartNew(async () => { await AsyncMethod(); });

对此:

    var t3 = AsyncMethod();

你已经任务了。无需通过在线程池上执行来包装它。

通过包装它,您可以将Task您已经拥有的(最终会出错)转换为Task<Task>永远不会出错的(只有它的Task.Result错误)。当然WaitAll,它对外部任务进行操作,因此它永远不会看到异常。

使用调试器单步执行此类代码以检查所有变量的运行时值是一种很好的技术。以这种方式发现这个错误比仅仅通过查看代码要容易得多(“查看”对我来说就足够了,因为我有这方面的经验 - 但如果我没有经验,我将需要运行这段代码并调试它)。

于 2012-12-31T13:41:32.757 回答
3

我想了解为什么 count1 和 count2 变量是 2 而不是 3,我怎样才能在 AsyncMethod 中获得第三个异常?

Task.Factory.StartNew返回一个基本的Task. 如果您将async委托传递给它,则返回的Task仅代表async方法的开始(直到它屈服于其调用者)。

您应该使用Task.Run代码asyncTask.Run将为委托创建一个Task包装器async,因此Task返回的 fromTask.Run代表整个async方法。

Stephen Toub 有一篇优秀的博客文章详细介绍了之间的区别Task.RunTask.Factory.StartNew

Task此外,正如 usr 所提到的,每当您阻塞而不是await在 GUI 或 ASP.NET 上下文中时,都会遇到死锁问题。我有一篇博文详细介绍了这个死锁问题。您应该使用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();
}
于 2012-12-31T15:40:44.203 回答