簡體   English   中英

如何將從異步函數引發的異常傳播到正在等待的異步void函數?

[英]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引發的異常,而無需更改調用鏈中的每個函數以使用asyncawait和返回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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM