同步执行异步功能

时间:2015-10-12 13:14:23

标签: c# winforms asynchronous async-await .net-4.5

我已就此主题进行了大量搜索,并且我在此网站上阅读了有关此主题的大部分帖子,但我仍然感到困惑,我需要一个直接的答案。这是我的情况:

我有一个已建立的Winform应用程序,我无法完成所有' async'。我现在被迫使用一个全部写为异步函数的外部库。

在我的申请中我有

/// <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更改为async
  • 调用&#34; MyAsyncDriverFunction&#34;以同步方式,在处理
  • 之后的代码之前,必须等待它完成所有工作
  • 能够处理这两个函数中的异常(从我到目前为止读到的这个很棘手吗?)
  • 我需要一种使用标准API的简单方法,我无法使用任何第三方库(即使我想这样做)

5 个答案:

答案 0 :(得分:5)

  然而,我仍然感到困惑,我需要一个直接的答案。

那是因为没有“直截了当”的答案

唯一正确的解决方案是使MySyncFunction异步。期。所有其他解决方案都是黑客攻击,并且没有任何黑客在所有情况下都能完美运行

我在my recent MSDN article on brownfield async development详细了解,但这里是要点:

您可以使用Wait()Result屏蔽。正如其他人所指出的那样,您很容易导致死锁,但如果异步代码永远不会在其捕获的上下文中恢复,则可以正常工作。

您可以将工作推送到线程池线程,然后阻止。但是,这假设异步工作能够被推送到其他任意线程,它可以在其他线程上恢复,从而可能引入多线程。

您可以将工作推送到执行“主循环”的线程池线程 - 例如,调度程序或我自己的AsyncContext类型。这假设异步工作能够被推送到另一个线程,但消除了对多线程的任何担忧。

您可以在主线程上安装嵌套的消息循环。这将在调用线程上执行异步代码,但也会引入reentrancy,难以正确推理。

简而言之,没有一个答案。每种方法都适用于不同类型的异步代码。

答案 1 :(得分:1)

简单地针对您的异步方法调用.Result.Wait将会死锁,因为您处于GUI应用程序的上下文中。请参阅https://msdn.microsoft.com/en-us/magazine/jj991977.aspx(章节&#39; Async All the Way&#39;)以获得更好的解释。

解决问题的方法并不容易,但Stephen Cleary详细介绍了这一问题:here

因此,您应该使用Nito.AsyncEx库(Nuget上提供)。 如果你真的无法添加他写入项目的库,你可以检查源代码并使用它的一部分,MIT许可证允许它。

答案 2 :(得分:0)

只需在方法调用结束时添加.Result调用。

var strCollection = MyAsyncDriverFunction().Result;

答案 3 :(得分:0)

我不确定专家会说些什么,但根据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)
    {
    }
}

会做你没有死锁的要求。关键是要隐藏&#34;异步任务执行期间的当前同步上下文并强制使用已知为Post方法使用线程池的默认同步上下文。同样,我不确定这是好事还是坏事以及它可能引入的副作用,但是一旦你问我,我就是在分享它。

答案 4 :(得分:-4)

尝试将“await AsyncExternalLibraryFunction1()”更改为“AsyncExternalLibraryFunction1()。Wait()”并在其旁边,然后删除  函数“MyAsyncDriverFunction”的异步