2009-06-19 53 views
4

我已经使用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中包装发生在堆栈下方的异常,或者类似的东西,所以我认为在这种情况下重新抛出异常是正确的。

回答

2

根据您引用的页面上讨论的ASync模式,这看起来像是包装异步调用的非常好的尝试。

是什么让我犹豫是方法'BeginConnect'的名称;一些.NET类具有'BeginXxx'/'EndXxx'对来处理异步调用,并且这些类通常指定正确操作要求'EndXxx'调用是从回调处理程序调用的,这是您的方案不支持的东西对于。

如果你打包的电话确实符合链接页面上讨论的模式,这应该工作,如果你打包的电话是第二种你不是那里的电话......

+0

我的代码仅用于符合基于事件的异步方法模式的方法 - 我的内部方法BeginConnect,其实现我没有显示,负责封装Begin/End调用。 – 2009-06-19 08:53:04