使用设置为ConcurrencyMode.Multiple和I​​nstanceContextMode.PerCall的WCF服务行为属性时是否可能出现并发问题?

时间:2010-03-30 14:02:37

标签: c# wcf multithreading nhibernate transactions

我们有一个WCF服务,可以进行大量的事务NHibernate调用。偶尔我们看到SQL超时,即使调用更新了不同的行,并且表被设置为行级锁定。

在深入挖掘日志之后,看起来不同的线程正在进入代码中的相同点(我们的事务使用块),并且更新挂起在提交上。但是,这没有意义,因为我们认为以下服务类属性强制每个服务调用一个唯一的执行线程:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]

我们最近将并发模式更改为ConcurrencyMode.Single并且还没有遇到任何问题,但是这个错误很难重现(如果有人有任何关于刷新这样的错误的想法,请告诉我!)。

无论如何,这一切都让我想到了一个问题:PerCall的InstanceContextMode是否应该在服务中强制执行线程安全,即使ConcurrencyMode设置为多个?如何通过同一个服务实例为两个调用提供服务?

谢谢!

4 个答案:

答案 0 :(得分:24)

让两个不同的WCF客户端(即代理)引用WCF服务的同一实例的唯一方法是使用InstanceContextMode=InstanceContextMode.Single。如果缩放是一个问题,这是一个糟糕的选择,所以如果可以的话,你想使用PerCall

当您使用PerCall时,每个 CALL 到WCF服务都会获得自己的WCF服务实例。没有共享服务实例,但这并不意味着它们不共享相同的后端存储(例如,数据库,内存,文件等)。请记住,PerCall允许每个 调用 同时访问您的WCF服务。

ConcurrencyMode设置控制服务本身的线程模型。设置Single限制所有WCF服务实例在同一线程上运行。因此,如果您同时连接多个客户端,则它们将仅在WCF服务端一次执行一个。在这种情况下,您可以利用WCF提供同步。正如您所看到的那样,它会正常工作,但我认为这只是对同步的宏级别控制 - 每个WCF服务调用将在下一次调用执行之前完整执行。

但是,将ConcurrencyMode设置为Multiple将允许所有WCF服务实例同时执行。在这种情况下,您负责提供必要的同步。可以将其视为对同步进行微级控制,因为您只能同步每个需要同步的调用的那些部分。

我希望我已经很好地解释了这一点,但这里是ConcurrencyMode的MSDN文档的片段,以防万一:

  

将ConcurrencyMode设置为Single可指示系统进行限制   一个线程的服务实例   一次执行,释放   你处理线程   的问题。值为Multiple意味着   服务对象可以执行   任何时候都有多个线程。在   在这种情况下,您必须确保线程   安全

修改

你问了

  

使用ConcurrencyMode.Single时,是否有使用PerCall与Single的性能提升?或者反之亦然?

这可能取决于服务。

使用InstanceContextMode.PerCall,通过代理为每个调用创建一个新的服务实例,因此您需要处理实例创建的开销。假设您的服务构造函数没有做太多,这不会是一个问题。

对于InstanceContextMode.Single,在应用程序的生命周期中只存在一个服务实例,因此实际上没有与实例创建相关的开销。但是,此模式仅允许一个服务实例处理将要进行的每个调用。因此,如果您同时进行多个呼叫,则每个呼叫都必须等待其他呼叫完成才能执行。

对于它的价值,这是我如何做到这一点。将PerCall实例上下文与Multiple并发一起使用。在WCF服务类中,创建静态成员以便为您管理后端数据存储,然后根据需要使用lock语句,volatile字段等同步对这些静态成员的访问。这允许您的服务可以很好地扩展,同时仍然保持线程安全。

答案 1 :(得分:7)

我认为答案是有多个线程(在客户端)使用相同的代理实例,因此可能允许多个调用进入同一个实例。这个post有更详细的解释。

答案 2 :(得分:2)

如果您不在服务器上使用双向回调,则

InstanceContextMode.PerCallConcurrencyMode.Single应该没问题。在这种情况下,您将需要使用ConcurrencyMode.Reentrant或回调将无法访问锁定的服务实例,并且将发生死锁。

由于每次调用服务实例创建,因此其他线程或调用无法访问它。正如在其他答案[{3}}中提到的文章中所述,如果会话是在绑定级别上创建的并且您使用相同的服务代理对象,那么这种组合仍然可能是个问题。

因此,如果您不使用相同的代理对象或没有会话绑定,并且不使用双向回调到客户端(最有可能它们应该是OneWay)InstanceContextMode.PerCallConcurrencyMode.Single应该好。

答案 3 :(得分:1)

我认为这完全取决于要求。 如果我们打算多次调用相同的服务,那么我们可以更好地使用 InstanceContextMode是Single,concurrencymode是多个。