前幾天在用執行緒池執行一些任務時運到一種情形,就是回撥方法中使用到了非同步方法,但是回撥方法貌似不支援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()
。