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 關注
- 81 瀏覽
添加回答
舉報