WCF ClientBase是线程安全的吗?

33

我已经实现了ClientBase来使用WCF连接到一个服务。然后我调用通道上的一个方法与服务进行通信。

base.Channel.CalculateSomething();

当运行多个线程时,这个调用是线程安全的吗?还是需要在其周围加锁?

谢谢。

4个回答

15

是的,它是线程安全的。然而,你需要知道当使用相同的ClientBase实例从多个线程调用CalculateSomething时,WCF会自动序列化CalculateSomething的执行。所以,如果你希望CalculateSomething并发运行,那么你就需要重新考虑你的设计。查看这个答案来了解一种为CalculateSomething方法创建异步API的方法。


3
你能解释一下你所说的:“但是,你应该知道当使用相同的ClientBase实例从多个线程调用CalculateSomething时,WCF会自动序列化执行。” 谢谢! - Erick
4
这句话的意思是,WCF会以顺序方式逐一进行调用,而不是同时进行(尽管从编程角度来看,它们似乎是同时进行的)。 - Raphaël Saint-Pierre
3
你说它是线程安全的证据或理由是什么?你怎么知道CalculateSomething是自动序列化的:是由客户端ClientBase实例序列化,还是在服务器/实现端序列化?我问这个问题是因为MSDN上的ClientBase类结尾处指出它不是线程安全的。 - ChrisW
@ChrisW:嗯……你让我开始怀疑我的答案了。实际上,文档完全与我的答案相矛盾。但另一方面,如果我没记错的话,这些调用确实是串行化的。天哪,这已经很久了,我对这个问题和以这种方式进行WCF的回忆模糊不清。 - Brian Gideon

15
这里的回答及后续评论让我也感到不确定,所以我进行了更深入的调查。这里有一些可靠的证据表明 ClientBase<T> 是线程安全的 - 这篇博客文章 讨论了如何使 WCF 服务在存在多个线程同时使用单个客户端代理时正常运行(粗体强调是原文):

... 然而有一种情况,当 PerCall 服务上的 ConcurrencyMode 设置为 Multiple 时,可以增加您的服务吞吐量,如果满足以下条件:

  1. 客户端是多线程的,并且从多个线程使用同一个代理对您的服务进行调用。

  2. 客户端和服务之间的绑定是具有会话的绑定(例如 netTcpBinding、具有可靠会话的 wsHttpBinding,netNamedPipeBinding 等)。

此外,这篇文章中的证据似乎与 Brian 的额外评论相矛盾,即 WCF 会序列化任何多线程请求。如果使用了 ConcurrencyMode.MultipleInstanceContextMode.PerCall,则该文章显示单个客户端的多个请求可以同时运行。
这里还有一些关于此方法性能影响以及一些替代方案的讨论。只在这里

2
虽然通道和由通道创建的客户端是线程安全的,但它们可能不支持同时向线路写入多个消息。 - Josef Bláha

3

是的,在通道上调用该方法是线程安全的(从客户端的角度来看——服务方面取决于服务实现)。您可以同时从多个线程中调用此方法。即使自动生成代理,也会为您提供创建异步调用方法的选项。


1
你说它是线程安全的证据或理由是什么?我问这个问题是因为在MSDN上的ClientBase类的结尾处,它说它不是线程安全的。 - ChrisW
@ChrisW:这可能意味着访问ClientBase的属性不是线程安全的,但对服务的调用是(或者文档中可能存在错误 - 这并不罕见)。首先,您可以在没有客户端基础的情况下进行调用 - 您只需要一个通道。我没有任何证据。我只是相信调用远程服务不需要存储任何全局共享数据 - 否则,整个WCF客户端端将设计得非常糟糕。 - Ladislav Mrnka
内部的“channel”是否被认为是线程安全的?我想象一个channel拥有一些内存缓冲区和网络套接字;除非它明确地设计支持并发用户(例如通过对它们的请求进行排序),否则如果两个用户同时尝试写入内存和套接字,那将是灾难性的。 - ChrisW
@ChrisW:Channel仅使用特定传输协议的常见.NET实现。例如,对于HTTP,它将为每个调用创建一个新的HttpWebRequest实例。我坚信进行调用是线程安全的操作,因为每个调用应该在隔离中处理 - 可能存在一些共享数据,例如安全上下文,但我希望这已经处理好了。更改ClientBase上的任何内容可能不是线程安全的 - 另一方面,如果我记得正确,有些更改只能在您进行第一次调用之前完成。 - Ladislav Mrnka
2
在过去的两年中,您是否将自己的“强烈信仰”和“希望”转变为“知道”?如果您有相关的主要来源,能够明确地表达这一点,那将是很好的。 - Scott Chamberlain
显示剩余3条评论

0
敬启者, 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
名称:对象一“你好”


1
这表明调用没有串行化。它不能证明客户端是线程安全的 - 这可能只是巧合。 - Josef Bláha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接