我正在测试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多次调用它时,需要aprox:call count * 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);
}
}
}
我已经尝试了Instancing和Concurrency(Instance mode = Per Call and Concurrency = Single
等)的所有9种组合。
有趣的是,如果我为所有Task创建新的ServiceClient对象,它工作正常,但我不认为这是正确的方法。我觉得必须有一些我错过的东西。如果是这样,你能告诉我到底是什么吗?
答案 0 :(得分:3)
问题出在客户端。
在对服务进行任何调用之前,您必须在Open()
对象上显式调用Service1Client
。否则,您的WCF客户端代理将在内部调用EnsureOpened()
。问题特别是EnsureOpened()
将导致每个请求在执行之前等待上一个请求完成,这就是为什么一次只发送一个请求而不是根据需要并行发送请求。
更改您的代码:
using (var service = new Service1Client())
{
service.Open();
// Do stuff...
}
来自 Wenlong Dong 优秀blog post的主题:
如果您没有先调用“打开”方法,则会打开代理 在代理上进行第一次调用时在内部。这就是所谓的 自动打开。为什么?当第一条消息通过自动打开发送时 代理,它将导致代理自动打开。您可以使用 .NET Reflector打开方法 System.ServiceModel.Channels.ServiceChannel.Call并查看以下内容 代码:
if (!this.explicitlyOpened) { this.EnsureDisplayUI(); this.EnsureOpened(rpc.TimeoutHelper.RemainingTime()); }
当您深入了解EnsureOpened时,您会看到它正在调用 CallOnceManager.CallOnce。对于非第一次通话,你会打 SyncWait.Wait等待第一个请求完成。这个 机制是确保所有请求都等待代理 打开并确保正确的执行顺序。所以 请求被序列化为单个执行序列,直到全部 请求从队列中排出。这不是理想的 大多数情况下的行为。