【发布时间】:2011-06-05 06:03:00
【问题描述】:
我已经实现了 ClientBase 以使用 WCF 连接到服务。然后我在通道上调用一个方法来与服务通信。
base.Channel.CalculateSomething();
这个调用线程是安全的还是应该在运行多个线程时锁定它?
谢谢
【问题讨论】:
标签: multithreading wcf
我已经实现了 ClientBase 以使用 WCF 连接到服务。然后我在通道上调用一个方法来与服务通信。
base.Channel.CalculateSomething();
这个调用线程是安全的还是应该在运行多个线程时锁定它?
谢谢
【问题讨论】:
标签: multithreading wcf
这里答案的后续 cmets 也让我不确定,所以我做了更多的挖掘。这里有一些确凿的证据表明 ClientBase<T> 是线程安全的 - this blog post 讨论了如何使 WCF 服务在多个线程同时使用单个客户端代理的情况下正确执行(粗体重点在原文):
...但是,如果满足以下条件,在 PerCall 服务上将 ConcurrencyMode 设置为 Multiple 可以增加服务的吞吐量:
客户端是多线程的,并从多个线程使用同一个代理调用您的服务。
客户端和服务之间的绑定是具有会话的绑定(例如,netTcpBinding、wsHttpBinding w/Reliable Session、netNamedPipeBinding等)。
此外,这篇文章中的证据似乎与 Brian 关于 WCF 序列化任何多线程请求的额外评论相矛盾。该帖子显示来自单个客户端的多个请求同时运行 - 如果 ConcurrencyMode.Multiple 和 InstanceContextMode.PerCall 被使用。
还有一些关于这种方法的性能影响以及一些替代方案的额外讨论 here。
【讨论】:
是的,它是线程安全的。但是,您应该知道,当使用相同的 ClientBase 实例从多个线程调用 WCF 时,WCF 将自动序列化 CalculateSomething 的执行。因此,如果您期望CalculateSomething 同时运行,那么您将不得不重新考虑您的设计。查看this answer,了解为CalculateSomething 方法创建异步API 的一种方法。
【讨论】:
是的,在通道上调用方法是线程安全的(从客户端的角度来看 - 服务的角度取决于服务的实现)。您可以从多个线程并行调用此方法。甚至自动生成代理也会让您为异步调用创建方法。
【讨论】:
ClientBase 的属性不是线程安全的,但对服务的调用是(或者它可能是文档中的错误——这并不罕见)。首先,您可以在没有客户群的情况下拨打电话——您只需要一个渠道。我没有任何证据。我只是相信调用远程服务不需要存储任何全局共享数据 - 否则 WCF 的整个客户端将设计得非常糟糕。
HttpWebRequest 实例。我坚信进行调用是线程安全的操作,因为每个调用都应该单独处理——可能有一些共享数据,例如安全上下文,但我希望能够处理。更改 ClientBase 上的任何内容可能不是线程安全的 - 另一方面,如果我没记错的话,一些更改只能在您第一次调用之前完成。
它可能关注的对象。 WCF 客户端库可以是线程安全的,至少在此配置中是这样。其他配置我没试过。
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IWcfCallbacksContract), Namespace = "http://wcf.applicatin.srv/namespace")]
public interface IWcfContract
{
[OperationContract]
CompositeReturnObject GetServerObject();
}
服务:
public CompositeReturnObject GetServerObject()
{
CompositeReturnObject ret = new CompositeReturnObject("Hello");
Thread.Sleep(10000); // Simulating long call
return ret;
}
客户:
private void GetData_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Task 1 start: " + DateTime.Now.ToString("HH:mm:ss"));
Task.Factory.StartNew(() => {
var res = _proxy.GetServerObject();
Console.WriteLine("Task 1 finish: " + DateTime.Now.ToString("HH:mm:s"));
Console.WriteLine(res.ToString());
return;
}
);
Thread.Sleep(2000);
Console.WriteLine("Task 2 start: " + DateTime.Now.ToString("HH:mm:ss"));
Task.Factory.StartNew(() => {
var res = _proxy.GetServerObject();
Console.WriteLine("Task 2 finish: " + DateTime.Now.ToString("HH:mm:s"));
Console.WriteLine(res.ToString());
return;
}
);
}
结果:
任务 1 开始时间:15:47:08
任务 2 开始:15:47:10任务 1 完成时间:15:47:18
名称:对象一“你好”任务 2 完成时间:15:47:20
名称:对象一“你好”
【讨论】: