WCF回调死锁

标签 wcf callback deadlock

我在调用客户端回调方法的 WCF 服务中遇到了问题。首先是服务:

[ServiceContract(
SessionMode = SessionMode.Required,
CallbackContract = typeof(IMarketObserver))]
public interface IServer
{
  ...
  [OperationContract]
  [FaultContractAttribute(typeof(WCFFaultDetail))]
  bool NotifyOnMarket(EnumMarkets marketID);
  ...
}

服务实现:

[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple,
UseSynchronizationContext = false)]
public sealed class Platform : IServer
{
 ...
 public bool NotifyOnMarket(EnumMarkets marketID)
 {
    try
    {
        IMarketObserver callback = OperationContext.Current.GetCallbackChannel<IMarketObserver>();
        if (subscribers.Contains(callback) == false)
        {
            subscribers.Add(callback);
        }
    }
    catch
    {
        return false;
    }
    //This call may cause a call to the callback method SendMarketData()!!
    callSomeMethod();
    return exchangeProxy.IsMarketIDValid();
}

回调合约:

public interface IMarketObserver
{
  [OperationContract(IsOneWay = true)]
  void SendMarketData(MarketData marketData);
}

此回调的客户端实现是:

[CallbackBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple,
    UseSynchronizationContext = false)]
public class MarketBase : 
    IServerCallback
{
  protected IService serviceProxy;
  public void SendMarketData(MarketData marketData)
  {
      //Do something
  }
  private void NotifyOnMarkets()
  {
      foreach (EnumMarkets item in observedMarkets)
      {
          try
          {
              bool res = serviceProxy.NotifyOnMarket(item);
          }
          catch (Exception e)
          {
             ...
          }
      }
  }

使用 foreach 循环调用 NotifyOnMarkets() 时会出现问题。

如果观察到的市场列表中只有一项,那么只有一次对服务的 NotifyOnMarket() 方法的调用,并且一切正常。

但是如果observedMarkets包含多个项目,那么NotifyOnMarket()将被服务器以高频率调用多次。

服务器上NotifyOnMarket()的实现调用一个方法,该方法又会调用回调方法SendMarketData(我对此事实进行了评论)。

在痕迹中我可以看到 serviceProxy.NotifyOnMarket(item);不返回第二个项目,就会发生超时。

在服务器端,对 NotifyOnMarket() 的多次调用被正确处理并退出该方法。但如上所述, bool 结果不会显示在客户端上(超时)。

此外,我们可以看到在服务器端调用了回调(它是单向的,因此不会返回任何响应),但在客户端,由于未调用回调实现,因此没有任何反应。

我的结论是发生了某种死锁,这可能是由于客户端实例锁定了自身,因此服务器无法调用回调方法。

将实例上下文类与同时执行服务调用的类分开是否更好?如果是这样,为什么?

感谢您的建议,

于尔根

最佳答案

对于你的问题:为什么它需要在不同的线程上? 阅读 WCF Duplex Messaging 的结尾:

WCF adds more complexity into the mix by enforcing a rule that says “unless you tell me otherwise I will only allow one thread at a time into an object that I control”. You see this with singleton services that will only allow one call at a time by default. The same is also true of the callback implementation object – so WCF will only allow one active thread in the client at a time. So while WCF is performing an outbound call it will not allow an inbound call into the object. This causes the initial problem with the deadlock that the service’s callback cannot be dispatched while the client’s outbound call is in progress. To solve this we use the “unless you tell me otherwise” part of the above rule. You do this by annotating the callback implementation class with a [CallbackBehavior] attribute like this:

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]

关于WCF回调死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7954498/

相关文章:

c# - 使用 NHibernate 和 CaSTLe WCF 工具在 WCF 上下文中处理 Rebus 消息

c# - IssuedTokenAuthentication AudienceUriMode =“从不”

javascript - Sequelize 调用结构

javascript - 如何从异步回调函数返回值?

javascript - 你能等待 javascript 回调吗?

WCF 数据合约解析器

c# - WCF 服务命名空间冲突,全部在同一个解决方案中

c - printf 死锁 - 如果在信号处理程序中使用 printf,则某些 printf 消息会被忽略

ruby-on-rails - 没有设置行锁或表锁,怎么会出现死锁呢? (由 rails ActiveRecord#touch 引起)

使用多线程代码在一张表上出现 MySQL 死锁