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

Multiple parallel calls to WCF Service takes longer than single call

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

有wcf服务:

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

}

从我的表单应用程序调用此服务方法。当我进行单次呼叫时,它需要 aprox: 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);
           }


        }
    }

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

有趣的是,如果我为所有任务创建新的 ServiceClient 对象,它工作正常,但我认为这不是正确的方法。我觉得我一定有什么东西 missed.If 所以,你能告诉我具体是什么吗?

问题出在客户端。

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

像这样更改您的代码:

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

来自董文龙的优秀blog post主题:

If you don’t call the “Open” method first, the proxy would be opened internally when the first call is made on the proxy. This is called auto-open. Why? When the first message is sent through the auto-opened proxy, it will cause the proxy to be opened automatically. You can use .NET Reflector to open the method System.ServiceModel.Channels.ServiceChannel.Call and see the following code:

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

When you drill down into EnsureOpened, you will see that it calls CallOnceManager.CallOnce. For non-first calls, you would hit SyncWait.Wait which waits for the first request to complete. This mechanism is to ensure that all requests wait for the proxy to be opened and it also ensures the correct execution order. Thus all requests are serialized into a single execution sequence until all requests are drained out from the queue. This is not a desired behavior in most cases.