真正的异步WCF服务

本文关键字:WCF 服务 异步 | 更新日期: 2023-09-27 18:13:51

我正在实现一个异步服务。在评估了微软的例子之后,我想知道他们的方法是否真的是异步的。我很确定是这样的,但是我在网上看到的一些样本和AsyncCallback参数让我很好奇。

根据示例,我们需要像这样实现BeginEnd方法对:
public IAsyncResult BeginGetAcmeAnvil(AsyncCallback callback, object state)
{
  // Starts synchronous task
  var acmeAsyncResult = new AcmeAsyncResult<Anvil>
  {
     Data = new Anvil()
  };      
  return acmeAsyncResult;
}
public Anvil EndGetAcmeAnvil(IAsyncResult result)
{
  var acmeAsyncResult = result as AcmeAsyncResult<Anvil>;
  return acmeAsyncResult != null
    ? acmeAsyncResult.Data
    : new Anvil();
}

非常简单,但是为什么我们有一个AsyncCallback参数呢?我们不应该调用callback,这将反过来触发End方法吗?

我是这么想的:

public delegate void AsyncMethodCaller(AcmeAsyncResult<Anvil> acmeAsyncResult, 
                                       AsyncCallback callback);
public IAsyncResult BeginGetAcmeAnvil(AsyncCallback callback, object state)
{
  var acmeAsyncResult = new AcmeAsyncResult<Anvil>();
  var asyncMethodCaller = new AsyncMethodCaller(GetAnvilAsync);
  // Starts asynchronous task
  asyncMethodCaller.BeginInvoke(acmeAsyncResult, callback, null, null);
  return acmeAsyncResult;
}
private void GetAcmeAnvilAsync(AcmeAsyncResult<Anvil> acmeAsyncResult,
                               AsyncCallback callback)
{
  acmeAsyncResult.Data = new Anvil();
  callback(acmeAsyncResult);  // Triggers EndGetAcmeAnvil
}
public Anvil EndGetAcmeAnvil(IAsyncResult result)
{
  var acmeAsyncResult = result as AcmeAsyncResult<Anvil>;
  return acmeAsyncResult != null
    ? acmeAsyncResult.Data
    : new Anvil();
}

我使用loadUI做了一些负载测试,但是没有明显的性能变化。

真正的异步WCF服务

我找到了一篇很好的文章,解释了如何从异步WCF服务中获得最佳性能。

要点是:

  1. 不要在Begin方法中做繁重的工作,和
  2. 做回调触发结束方法。

以下是原文节选:

为获得最佳性能,在调用/实现上述异步模式时遵循以下两个原则:

  • 原则1: 不要在Begin方法中做繁重的工作…

    这样做的原因是您应该尽快返回调用线程,以便调用者可以安排其他工作。如果是UI线程,则应用程序需要使用该线程响应用户输入。

  • 原则2: 避免在Begin方法的同一线程上调用End方法。

    End方法通常是阻塞的。它等待操作完成。如果你实现End方法,你会看到它实际上调用了IAsyncResult.WaitHandle.WaitOne()。另一方面,作为一个正常的实现,这个WaitHandle是一个延迟分配的ManualResetEvent。只要你不调用它,它就不会被分配。对于快速操作,这是相当便宜的。但是,一旦调用了End,就必须分配它。调用End的正确位置是来自操作的回调。当回调被调用时,这意味着阻塞工作真正完成。此时,您可以调用End来检索数据,而不会牺牲性能。

我认为它这样分开的主要原因是WCF运行时处理线程同步,而不是你必须手动处理它。

如果您通过回调调用end方法,则必须处理同步,这会使模式变得相当复杂(正如您在编码示例中看到的那样)。此模式的目的不是让您真正了解线程的东西,您只是想编写长时间运行的操作,而不必考虑线程的实现细节。