【问题标题】:Multilevel asynchronous method call pattern in c#c#中的多级异步方法调用模式
【发布时间】:2010-06-10 07:07:43
【问题描述】:

我在异步调用方法方面遇到了设计问题。

我想知道调用异步方法的最佳/好的模式,它调用另一个异步方法,它调用另一个异步方法:)

换句话说,我有使用异步方法创建的 WCF 服务引用,我想从另一个异步方法调用它们,该方法由其他异步方法调用。 所有这些都是为了非阻塞 GUI。

谢谢!

【问题讨论】:

    标签: c# .net wcf asynchronous


    【解决方案1】:

    如果您的唯一目标是拥有一个非阻塞的 GUI,那么多个级别是不必要的。一旦您的顶级方法在后台运行,您的 GUI 就会被释放。使用多个级别确实会带来额外的复杂性。

    可能有其他原因(性能)来异步调用较低级别的方法,但这取决于。以后需要等结果吗?

    所以我认为这里没有“模式”。

    【讨论】:

    • 虽然完成后“与最终结果会合”可能需要一点努力。
    • 架构需要多个级别。这是关注点分离。这个呢?
    • @michajas:多层次是关注点分离。使它们异步增加了复杂性和混合关注点(IAsyncResult)
    • @Henk:好的,那么您认为更好的方法是使 WCF 调用同步并仅使顶部调用(从 GUI 调用)异步?
    • @michajas:一般来说:是的。可能会有例外,您必须提供更多详细信息。
    【解决方案2】:

    在诸如 MonoRail 或 MS MVC 之类的 MVC 框架中创建异步控制器时,最好有多个级别的一个示例。最终调用某些 IO 阻塞的东西,例如来自 System.Data.SqlClient 或某个套接字的 SqlCommand,会将 IO 操作数放在 IO 完成端口上:http://msdn.microsoft.com/library/aa365198,这将保存托管/非托管线程的量用于某些东西更有用。

    如果您编写返回 IAsyncResult 的类,那么您离实现协程不远了。这是一篇关于如何将异步编程与协程一起使用的好文章:http://blogs.msdn.com/b/pfxteam/archive/2009/06/30/9809774.aspx

    Caliburn,一个 WPF 框架,原生支持协程。与 .Net 4 一起发布的任务并行库为其任务提供了 IAsyncResult 接口。 [如果您是 3.5,那么您可能需要创建自己的实现(它们制作起来非常简单,只需实现接口)。] 协程是一种使用 IEnumerable 的编译器重写来将 IAsyncResults 推送到一堆要做的事情(从“异步管理器”中看到)。

    F# async(如在否决的答案中所见)使用 monad(与它们在 CLR 上一样的 monadic)将异步请求的状态从 Begin* 移动到 End* 方法。编译器都将其转换为嵌套的 lambda 表达式/SelectMany。

    【讨论】:

      【解决方案3】:

      我最近开发了调用后端 Web 服务以检索数据的 ADFS 属性存储实现。我想遵循工厂 -> 客户端方法,而不是为每个调用重用客户端,所以我最终进行了 2 级异步调用,如下面的简化代码示例所示:

      public class IMyAttributeStore : IAttributeStore
      {
          ChannelFactory<IMyBackendInterface> factory;
          public IMyAttributeStore()
          {
          }
      
          public IAsyncResult BeginExecuteQuery(string query, string[] parameters, AsyncCallback callback, object state)
          {
              AsyncResult queryResult = new TypedAsyncResult<string[][]>(callback, state);
              var client = factory.CreateChannel();
              CallState cs = new CallState(client, queryResult);
      
              Request rq = new Request();
      
              client.BeginGetUserRoles(rq, new AsyncCallback(AsyncCallCallback), cs);
      
              return cs.result;
          }
      
          public string[][] EndExecuteQuery(IAsyncResult result)
          {
              return TypedAsyncResult<string[][]>.End(result);
          }
      
          // Initialize state here.
          public void Initialize(Dictionary<string, string> config)
          {
              var endpoint = config["endpointConfigurationName"];
              factory = new ChannelFactory<IMyBackendInterface>(endpoint);
          }
      
          void AsyncCallCallback(IAsyncResult result)
          {
              CallState cs = (CallState)result.AsyncState;
      
              Response data = cs.client.EndGetUserRoles(result);
              List<string[]> claimData = new List<string[]>();
              foreach (var val in data.Values)
                  claimData.Add(new string[1] { val });
              string[][] retVal = claimData.ToArray();
      
              TypedAsyncResult<string[][]> queryResult = (TypedAsyncResult<string[][]>)cs.result;
              queryResult.Complete(retVal, false);
          }
      }
      
      class CallState
      {
          public IMyBackendInterface client;
          public AsyncResult result;
      
          public CallState(IMyBackendInterface c, AsyncResult r)
          {
              client = c;
              result = r;
          }
      }
      

      想知道这是一个可以遵循的好模式,还是有人同时发现了更好的模式?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-18
        • 2019-01-26
        • 1970-01-01
        相关资源
        最近更新 更多