繁体   English   中英

从复杂的任务链中捕获异常

[英]Catching exceptions from complex chains of tasks

我有一个 .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 如何捕获任务引发的异常?

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

编辑: 这是我的代码版本的链接,异常应该正确“冒泡”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM