对WCF服务的多个并行调用比单个调用花费更长的时间

本文关键字:调用 时间 单个 WCF 并行 服务 | 更新日期: 2023-09-27 17:59:36

我正在测试WCF并发和实例化。

有wcf服务:

public class Service1 : IService1
{
    public string GetData(int value)
    {
        Thread.Sleep(1000);
        return string.Format("You entered: {0}", value);
    }
}

在我的表单应用程序中,我调用了此服务方法。当我打一个电话时,它需要大约1秒的时间。

    private void single_Click(object sender, EventArgs e)
    {
        using (var service = new Service1Client())
        {
            var sw = new Stopwatch();
            sw.Start();
            service.GetData(1);
            sw.Stop();
            Debug.WriteLine(sw.Elapsed);
        }
    }

但是,当我用Tasks多次调用它时,它需要大约1秒钟:调用计数*。

    private void mult_Click(object sender, EventArgs e)
    {
        using (var service = new Service1Client())
        {
           var tasks = new List<Task<string>>();
           for (var i = 0; i < 5; i++)
           {
              int p = i;
              tasks.Add(Task.Factory.StartNew(() => service.GetData(p)));
           }

           var sw = new Stopwatch();
           sw.Start();
           Task.WaitAll(tasks.ToArray());
           sw.Stop();
           Debug.WriteLine(sw.Elapsed);
           foreach (var task in tasks)
           {
               Debug.WriteLine(task.Result);
           }

        }
    }

我已经尝试了实例化和并发(Instance mode = Per Call and Concurrency = Single等)的所有9种组合

有趣的是,若我为所有Task创建新的ServiceClient对象,它会很好地工作,但我认为这不是正确的方法。我觉得一定有什么东西我错过了。如果是,你能告诉我具体是什么吗?

对WCF服务的多个并行调用比单个调用花费更长的时间

问题出在客户端。

在对服务进行任何调用之前,必须在Service1Client对象上显式调用Open()。否则,WCF客户端代理将在内部调用EnsureOpened()。问题特别是EnsureOpened()将导致每个请求在执行之前等待,直到前一个请求完成,这就是为什么一次只发送一个请求,而不是按照需要并行发送。

更改您的代码如下:

using (var service = new Service1Client())
{
    service.Open();
    // Do stuff...
}

来自Wenlong Dong关于主题的优秀博客文章

如果你不先调用"Open"方法,代理就会被打开在代理上进行第一次调用时在内部执行。这叫做自动打开。为什么?当通过自动打开发送第一条消息时代理,它将导致代理自动打开。您可以使用.NET反射器打开方法System.ServiceModel.Channels.ServiceChannel.Call,请参阅以下内容代码:

if (!this.explicitlyOpened)
  {
         this.EnsureDisplayUI();
         this.EnsureOpened(rpc.TimeoutHelper.RemainingTime());
  } 

当你深入EnsureOpened时,你会看到它调用CallOnceManager.CallOnce。对于非首次呼叫,您会点击SyncWait。等待第一个请求完成的等待。这机制是确保所有请求都等待代理打开,它还确保了正确的执行顺序。因此请求被序列化为单个执行序列,直到所有请求从队列中排出。这不是所期望的大多数情况下的行为。