1 回答
TA贡献1779条经验 获得超6个赞
async与服务员一起工作,而不是与任务一起工作。因此,在方法末尾需要一些额外的逻辑来将等待者的状态转换为任务。
您认为没有延续的假设是错误的。如果您刚刚返回任务,那将是正确的:
Task DelayAsync()
{
return Task.Delay(3000);
}
但是,当您将方法标记为 时,事情会变得更加复杂async。方法的一个重要属性async是它处理异常的方式。例如考虑这些方法:
Task NoAsync()
{
throw new Exception();
}
async Task Async()
{
throw new Exception();
}
现在如果你调用它们会发生什么?
var task1 = NoAsync(); // Throws an exception
var task2 = Async(); // Returns a faulted task
不同之处在于异步版本将异常包装在返回的任务中。
它与我们的案例有什么关系?
当您await使用方法时,编译器实际上会调用GetAwaiter()您正在等待的对象。等待者定义了 3 个成员:
该IsCompleted物业
OnCompleted方法_
GetResult方法_
可以看到,没有成员直接返回异常。如何知道服务员是否有故障?要知道这一点,您需要调用GetResult将抛出异常的方法。
回到你的例子:
async Task DelayAsync()
{
await Task.Delay(3000);
}
如果Task.Delay抛出异常,async机器需要将返回任务的状态设置为故障。要知道是否Task.Delay抛出异常,需要在完成GetResult后调用等待Task.Delay者。因此你有一个延续,虽然在看到代码时并不明显。在幕后,异步方法看起来像:
Task DelayAsync()
{
var tcs = new TaskCompletionSource<object>();
try
{
var awaiter = Task.Delay(3000).GetAwaiter();
awaiter.OnCompleted(() =>
{
// This is the continuation that causes your deadlock
try
{
awaiter.GetResult();
tcs.SetResult(null);
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
}
catch (Exception ex)
{
tcs.SetException(ex);
}
return tcs.Task;
}
实际代码比较复杂,用aAsyncTaskMethodBuilder<T>代替了TaskCompletionSource<T>,但是思路是一样的。
- 1 回答
- 0 关注
- 55 浏览
添加回答
举报