簡體   English   中英

同步執行異步函數

[英]Executing async function synchronously

我已經對這個主題進行了大量搜索,並且我閱讀了本網站上關於這個主題的大部分帖子,但是我仍然感到困惑,我需要一個直接的答案。 這是我的情況:

我有一個已建立的 Winform 應用程序,我不能讓它全部“異步”。 我現在被迫使用一個全部編寫為異步函數的外部庫。

在我的應用程序中,我有

/// <summary>
/// This function I can't change it to an 'async'
/// </summary>
public void MySyncFunction()
{
    //This function is my point in my application where I have to call the
    //other 'async' functions but I can't change the function itself to 'async'

    try
    {
        //I need to call the MyAsyncDriverFunction() as if it is a synchronous function
        //I need the driver function to finish execution and return before processing the code that follows it
        //I also need to be able to catch any exceptions
        MyAsyncDriverFunction(); 

        //Rest of the code have to wait for the above function to return
    }
    catch (Exception exp)
    {
        //Need to be able to handle the exception thrown 
        //from the MyAsyncDriverFunction here.  
    }
}

public static async Task<IEnumerable<string>> MyAsyncDriverFunction()
{
    try
    {
        var strCollection = await AsyncExternalLibraryFunction1();
        var strCollection2 = await AsyncExternalLibraryFunction2();

        return strCollection;
    }
    catch (Exception exp)
    {
        //Need to be able to catch an exception and re-throw it to the caller function
    }
}

如代碼中所述,我需要能夠:

  • 我無法將 MySyncFunction 更改為異步
  • 以同步方式調用“MyAsyncDriverFunction”,在我處理后面的代碼之前,它必須等待它完成所有工作
  • 能夠處理兩個函數中的異常(從我目前閱讀的內容來看,這很棘手?)
  • 我需要一種使用標准 API 的簡單方法,我不能使用任何第三方庫(即使我想使用)

但是我仍然很困惑,我需要一個直接的答案。

那是因為沒有“直截了當”的答案

唯一合適的解決方案是使MySyncFunction異步。 時期。 所有其他解決方案都是 hack,並且沒有在所有場景中都能完美運行的 hack

我在最近的 MSDN 文章中詳細介紹了棕地異步開發,但要點如下:

您可以使用Wait()Result進行阻塞。 正如其他人所指出的,您很容易導致死鎖,但如果異步代碼永遠不會在其捕獲的上下文中恢復,則這可以起作用。

您可以將工作推送到線程池線程,然后阻塞。 然而,這假設異步工作能夠被推送到某個其他任意線程並且它可以在其他線程上恢復,因此可能會引入多線程。

您可以將工作推送到執行“主循環”的線程池線程 - 例如,調度程序或我自己的AsyncContext類型。 這假設異步工作能夠被推送到另一個線程,但消除了對多線程的任何擔憂。

您可以在主線程上安裝嵌套消息循環。 這將在調用線程上執行異步代碼,但也會引入可重入性,這是非常難以正確推理的。

簡而言之,沒有一個答案 每一種方法都是一種適用於不同類型異步代碼的技巧。

簡單地針對異步方法調用.Result.Wait將會死鎖,因為您處於 GUI 應用程序的上下文中。 請參閱https://msdn.microsoft.com/en-us/magazine/jj991977.aspx (“一路異步”一章)以獲得很好的解釋。

解決您的問題並不容易,但 Stephen Cleary 已對其進行了詳細描述:這里

所以你應該使用 Nito.AsyncEx 庫(在Nuget可用)。 如果您真的無法將他編寫的庫添加到您的項目中,您可以檢查源代碼並使用其中的一部分,MIT 許可證允許這樣做。

只需在方法調用的末尾添加一個.Result調用。

var strCollection = MyAsyncDriverFunction().Result;

我不確定專家會說什么,但根據Stephen Cleary 的建議,我最終得出了以下想法。 有以下課程

public sealed class AsyncTask
{
    public static void Run(Func<Task> asyncFunc)
    {
        var originalContext = SynchronizationContext.Current;
        bool restoreContext = false;
        try
        {
            if (originalContext != null && originalContext.GetType() != typeof(SynchronizationContext))
            {
                restoreContext = true;
                SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            }
            var task = asyncFunc();
            task.GetAwaiter().GetResult();
        }
        finally
        {
            if (restoreContext) SynchronizationContext.SetSynchronizationContext(originalContext);
        }
    }
    public static TResult Run<TResult>(Func<Task<TResult>> asyncFunc)
    {
        var originalContext = SynchronizationContext.Current;
        bool restoreContext = false;
        try
        {
            if (originalContext != null && originalContext.GetType() != typeof(SynchronizationContext))
            {
                restoreContext = true;
                SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
            }
            var task = asyncFunc();
            return task.GetAwaiter().GetResult();
        }
        finally
        {
            if (restoreContext) SynchronizationContext.SetSynchronizationContext(originalContext);
        }
    }
}

並按如下方式使用它

public void MySyncFunction()
{
    try
    {
        AsyncTask.Run(() => MyAsyncDriverFunction()); 
    }
    catch (Exception exp)
    {
    }
}

會在沒有僵局的情況下做你所要求的。 關鍵是在異步任務執行期間“隱藏”當前同步上下文,並強制使用默認同步上下文,該上下文已知將線程池用於Post方法。 同樣,我不確定這是好主意還是壞主意,也不確定它會帶來什么副作用,但是一旦你問了,我只是分享它。

嘗試將“await AsyncExternalLibraryFunction1()”更改為“AsyncExternalLibraryFunction1().Wait()”並在其旁邊,並刪除函數“MyAsyncDriverFunction”的異步

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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