[英]Invoke event asynchronously and catching exceptions without stopping other tasks from running
[英]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.