![](/img/trans.png)
[英]What's the difference between awaiting async Task function and calling await inside a void function?
[英]How can I propagate an exception thrown from an asynchronous function to an awaiting async void function?
我有一長串的調用,這些調用最終會在另一個程序集中調用異步函數。 我希望此函數同步執行,並且可能會引發異常,該異常要在調用鏈中傳播。
下面的代碼片段最小化了這種情況:
static Task<int> Exc()
{
throw new ArgumentException("Exc");
return Task.FromResult(1);
}
static async void DoWork()
{
await Exc();
}
static void Main(string[] args)
{
try
{
DoWork();
}
catch (Exception e)
{
Console.WriteLine("Caught {0}", e.Message);
}
}
此代碼將導致崩潰,因為從Exc
引發的異常不會傳播回Main
。
我有什么辦法可以讓Main
的catch塊處理從Exc
引發的異常,而無需更改調用鏈中的每個函數以使用async
, await
和返回Task
?
在我的實際代碼中,此異步函數是從非常深層的函數調用鏈中調用的,所有這些函數調用應同步執行。 當它們永遠不會被使用(也不能安全地使用)時,使它們全部異步似乎是一個糟糕的主意,如果可能的話,我想避免這樣做。
根據MSDN ,沒有。
您主要使用void返回類型來定義需要該返回類型的事件處理程序。 返回void的異步方法的調用者無法等待它,也無法捕獲該方法引發的異常。
此代碼將導致崩潰,因為從Exc引發的異常不會傳播回DoWork。
一點也不。 來自Exc
的異常將傳遞給DoWork
,如果您在DoWork
進行了try
/ catch
,則可以在其中捕獲該異常。
您看到崩潰的原因是因為DoWork
正在傳播該異常,而DoWork
是一個async void
方法。 可以通過使DoWork
成為async Task
方法來輕松避免這種情況(請注意, async void
僅應用於事件處理程序,而DoWork
顯然不是)。 正如我在有關最佳做法的MSDN文章中所描述的那樣,努力避免async void
。
在我的實際代碼中,此異步函數是從非常深層的函數調用鏈中調用的,所有這些函數調用應同步執行。 當它們永遠不會被使用(也不能安全地使用)時,使它們全部異步似乎是一個糟糕的主意,如果可能的話,我想避免這樣做。
該操作要么是異步的,要么不是。 由於您正在調用的低級API是異步的,因此最好是代碼以異步方式使用它。 嘗試將異步代碼包裝在同步代碼中非常容易出錯,這是一個可怕的想法。 雖然有時有必要。
因此, 最干凈的解決方案是使異步方法具有異步簽名。 是的,這意味着要“一直保持異步 ”,正如我在有關異步最佳實踐的MSDN文章中所述。 但是,如果您喜歡通過異步進行同步,那么可以選擇我在我最近的“ Brownfield async”文章中描述的各種黑客之一 。
如果您堅持不讓整個堆棧異步,那么我將提供斯蒂芬斯答案的反駁:
會使DoWork() { Exec().Wait(); }
DoWork() { Exec().Wait(); }
為您工作?
控制台應用程序中沒有異步死鎖。 如果這不是控制台應用程序,則可以使用Ivan鏈接到的應用程序,或者使用ConfigureAwait(false)
或使用Task.Run(...).Wait()
不會死鎖,因為任務主體沒有同步上下文。
請注意,通過執行任何上述操作,異步IO的任何潛在收益都將丟失。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.