【问题标题】:Is this method of converting an asynchronous method to a synchronous one correct?这种将异步方法转换为同步方法的方法是否正确?
【发布时间】:2010-11-04 06:07:28
【问题描述】:

我有一个使用Event-based Asynchronous method pattern 实现的方法。我还想提供该方法的同步版本,但不想重写它(因为该方法涉及从 Silverlight 调用 WCF,所以异步版本必须是主要方法)。

我想出了以下通用方法将基于事件的异步调用转换为同步调用:

 Func<TArg1, TArg2, TArg3, TEventArgs> 
    CreateSynchronousMethodFromAsync<TArg1, TArg2, TArg3, TEventArgs>(
       Action<TArg1, TArg2, TArg3, EventHandler<TEventArgs>> asyncMethod) 
         where TEventArgs : AsyncCompletedEventArgs
    {
        Func<TArg1, TArg2, TArg3, TEventArgs> syncMethod = (arg1, arg2, arg3) =>
         {
             TEventArgs eventArgs = null;

             using (var waitHandle = new ManualResetEvent(false))
             {
                 asyncMethod(arg1, arg2, arg3, (sender, e) =>
                                          {
                                              eventArgs = e;
                                              waitHandle.Set();
                                          });

                 waitHandle.WaitOne();

                 return eventArgs;
             }
         };

        return syncMethod;
    }

所以如果我有这个异步方法:

void ConnectAsync(string address, 
     string userName, 
     string password, 
     EventHandler<ConnectCompletedEventArgs> completionCallback)

我可以将它转换成这样的同步调用:

public void Connect(string address, string userName, string password)
{
    Func<string, string, string, ConnectCompletedEventArgs> connect = 
        CreateSynchronousMethodFromAsync<string, string, string, ConnectCompletedEventArgs>(ConnectAsync);

    var connectResult = connect(address, userName, password);

    if (connectResult.Error != null)
    {
        throw connectResult.Error;
    }
}

我担心的是在闭包中捕获的 eventArgs 变量的使用。它被设置在一个线程中,并从另一个线程访问。我对 ManualResetEvent 的使用是否足以保证在发出事件信号后正确读取值,还是我需要做其他事情?

好了,您可能想在这里评论一下异常的处理。我的计划是 Async 方法将在 ConnectionException 或类似的东西中包装在堆栈下方发生的异常,所以我的想法是在这种情况下重新抛出异常是正确的。

【问题讨论】:

    标签: c# multithreading asynchronous closures


    【解决方案1】:

    根据您提到的页面上讨论的异步模式,这看起来是包装异步调用的一个很好的尝试。

    让我犹豫的是方法的名称'BeginConnect';一些 .NET 类具有“BeginXxx”/“EndXxx”对来处理异步调用,这些通常指定正确的操作要求从回调中调用“EndXxx”调用处理程序,这是您的方案无法满足的。

    如果您要包装的调用确实符合链接页面上讨论的模式,这应该可以工作,如果您要包装的调用是第二种调用之一,那么您并不完全在那里......

    【讨论】:

    • 我的代码仅设计用于符合基于事件的异步方法模式的方法 - 我的内部方法 BeginConnect,我没有展示它的实现,负责包装 Begin/End来电。
    • 请注意,您可能希望将其作为内部异常包装到新异常中,以便保留原始异常的堆栈跟踪,而不是 'throw connectionResult.Error'。
    • @Sam:我建议根据链接文章中的约定命名该方法,然后避免混淆......改为“ConnectAsync”。
    猜你喜欢
    • 2021-04-12
    • 2019-12-26
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-28
    相关资源
    最近更新 更多