【问题标题】:Catching exceptions from complex chains of tasks从复杂的任务链中捕获异常
【发布时间】:2021-03-25 07:24:10
【问题描述】:

我有一个 .Net 核心应用程序,它基本上做这样的事情:

public async Task<IOBoundStuffResult> DoIOBoundStuff()
{
    // Do IO bound stuff ...
    
    return new IOBoundStuffResult
    {
        Id = getIdForThing()
    };
}

public async Task<OtherIOBoundStuffResult> DoOtherIOBoundStuff()
{
    // Do other IO bound stuff ...
    
    return new OtherIOBoundStuffResult
    {
        Uri = getUriForThing()
    };
}

public async Task<IOBoundTaskResult> DoIOBoundStuff()
{
    var ioBoundTask1 = doIOBoundStuff();
    var ioBoundTask2 = doOtherIOBoundStuff();
    
    return await Task.WhenAll(ioBoundTask1, ioBoundTask2)
        .ContinueWith((task) =>
        {
            var id = ioBoundTask1.Result.Id;
            var uri = ioBoundTask2.Result.Uri;
            
            doSomethingWithIdAndUri(id, uri);
            
            return new IOBoundTaskResult
            {
                Id = id,
                Uri = uri
            };
        });
}

public async Task<IActionResult> DoThing()
{
    try
    {
        var cpuBoundTask = Task.Run(() =>
        {
            doCPUBoundStuff();
        });
        
        var ioBoundTask = DoIOBoundStuff();
        
        // do stuff with ioBoundTask, cpuBoundTask
    }
    catch (System.Exception ex)
    {
        // Process System.Exception.AggregateException, other exceptions
    }
}

这里的问题是,如果其中一项任务引发异常(特别是 doSomethingWithIdAndUri()),那么 try...catch 块不会捕获异常并导致崩溃。我尝试使用TaskContinuationOptions.OnlyOnFaulted 创建继续任务来处理异常,但似乎所做的一切总是导致TaskCancelledException 被抛出。如何捕获任务引发的异常?

【问题讨论】:

  • 等待他们? [添加字符以满足要求]
  • @PeterBons 是的,我正在等待 cpuBoundTask、ioBoundTask,如果这就是你的意思。
  • 我看到一个似乎没有等待的 Task.Run,​​对吗?

标签: c# exception .net-core async-await task-parallel-library


【解决方案1】:

为了让任务的异常“冒泡”,必须等待它。如果在等待时抛出了异常,则该异常将在当前上下文中重新抛出(尽管它有时可能被包装在另一种异常类型中,例如 AggregateException

您需要将var ioBoundTask = DoIOBoundStuff(); 更改为var ioBoundTask = await DoIOBoundStuff();,或者通过等待任务并将等待包装在另一个try-catch 块中来捕获其他地方的异常。

注意等待每一个异步方法。避免使用async void,因为您无法等待它们。请改用async Task

编辑添加: 在异步上下文中,您对 ContinueWith 的使用似乎有点奇怪。而不是

return await Task.WhenAll(ioBoundTask1, ioBoundTask2)
        .ContinueWith((task) =>
        {
            var id = ioBoundTask1.Result.Id;
            var uri = ioBoundTask2.Result.Uri;
            
            doSomethingWithIdAndUri(id, uri);
            
            return new IOBoundTaskResult
            {
                Id = id,
                Uri = uri
            };
        });

你可以写类似的东西

var result1 = await ioBoundTask1;
var result2 = await ioBoundTask2;

var id = result1.Id;
var uri = result2.Uri;
            
doSomethingWithIdAndUri(id, uri);
            
return new IOBoundTaskResult
{
    Id = id,
    Uri = uri
};

这样可以更清楚地了解正在发生的事情,并且可以更轻松地避免诸如异常被抛出/根本没有抛出的问题。在某些情况下,需要更明确地使用延续,但通常应该避免使用更简洁、更简洁的 async/await 语法。

编辑:Here's a link to my version of the code,异常应该正确“冒泡”。

【讨论】:

  • 是的,问题在于 doSomethingWithIdAndUri 是异步无效的。改变它并等待它解决了这个问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-02
  • 1970-01-01
  • 1970-01-01
  • 2013-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多