前幾天在用線程池執行一些任務時運到一種情形,就是回調方法中使用到了異步方法,但是回調方法貌似不支持async await的寫法。這時候我應該如何處理呢?是使用Task.Result來獲取返回結果,還是使用GetAwaiter.GetResult()呢?本文就來探討下吧。
作者:依樂祝
這里先上我這種場景的偽代碼:
ThreadPool.QueueUserWorkItem(ExcuteScanProcess, node);
在ExcuteScanProcess這個回調方法中
private void ExcuteScanProcess(object state)
{
……其他處理……
repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
……其他處理……
}
如上圖所示repository.UpdateAsync(node)屬於一部方法,這時候我想要等待它異步執行完成之后再執行后續的邏輯。這時候我有兩種選擇,是直接
repository.UpdateAsync(node).ConfigureAwait(false).GetAwaiter().GetResult();
好呢,還是
repository.UpdateAsync(node).ConfigureAwait(false).Result;
好呢?
為此我查找了相關的資料,對它倆的區別做一個簡單的總結:
其實這兩個使用方式是差不多的。不過,還是有一點小小的區別的:如果任務失敗,Task.GetAwaiter().GetResult()會直接拋出異常,而Task.Result則會把異常包裝在AggregateException中。從這個角度說Task.GetAwaiter().GetResult()要優於Task.Result。畢竟它少了異常的包裝操作,即直接拋出異常,而不是把異常包裝在AggregateException中。
下面的引言解釋了為什么Task.Result不僅僅包含Task.GetAwaiter().GetResult()(由於“非常高的兼容性”)的異常傳播行為。
如前所述,我們有一個非常高的兼容性標准,因此我們避免了改動。因此,
Task.Wait保留了始終包裝的原始行為。但是,您可能會發現自己處在某些高級情況下,這些情況下您想要的行為類似於所采用的同步阻塞Task.Wait,但是您希望將原始異常展開而不是傳播,而不是將其封裝在AggregateException中。為此,您可以直接定位任務的等待者。當您編寫“await task;”時,編譯器Task.GetAwaiter()會將其轉換為方法的用法,這將返回具有GetResult()方法的實例。當用於有故障的任務時,GetResult()將傳播原始異常(這是“await task;” 如何獲得其行為)。因此,您可以使用“task.GetAwaiter().GetResult()如果您想直接調用此傳播邏輯。
https://blogs.msdn.microsoft.com/pfxteam/2011/09/28/task-exception-handling-in-net-4-5/
“
GetResult”實際上表示“檢查任務是否有錯誤”通常,我會盡力避免對異步任務進行同步阻塞。但是,在少數情況下,我確實違反了該准則。在那些罕見的情況下,我的首選方法是
GetAwaiter().GetResult()因為它保留任務異常,而不是將它們包裝在中AggregateException。
總結
通過上述內容的闡述,因此在那些必須對異步任務進行同步阻塞的場景中,我選擇使用GetAwaiter().GetResult()。
