WCF回调死锁

WCF回调死锁,wcf,callback,deadlock,Wcf,Callback,Deadlock,我遇到了一个WCF服务问题,该服务在客户端上调用回调方法。首先是服务: [ServiceContract( SessionMode = SessionMode.Required, CallbackContract = typeof(IMarketObserver))] public interface IServer { ... [OperationContract] [FaultContractAttribute(typeof(WCFFaultDetail))] bool No

我遇到了一个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();
}
此回调的客户端实现是:

[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()时会出现问题

如果observedMarkets列表中只有一项,那么只需调用服务的NotifyOnMarket()方法一次,一切都正常

但是,如果ObservedMarket包含多个项目,则NotifyOnMarket()将被频繁地多次调用到服务器

服务器上NotifyOnMarket()的实现调用了一个方法,该方法将调用回调方法SendMarketData(我对这个事实进行了注释)

在跟踪中,我可以看到serviceProxy.NotifyOnMarket(item);不返回第二项,则会发生超时

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

此外,您可以看到,在服务器端调用了回调(这是单向的,因此不会返回响应),但在客户端,由于没有调用回调实现,因此不会发生任何事情

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

将实例上下文类与同时执行服务调用的类分开是否更好?若然,原因为何

谢谢你的建议


Juergen

回答您的问题:为什么它需要在不同的线程上? 阅读以下内容的结尾:

WCF通过强制执行一条规则增加了混合的复杂性 “除非你告诉我,否则我一次只允许一个线程 进入我控制的对象”。您可以在singleton服务中看到这一点 默认情况下,一次只允许一个调用。这也是同样的道理 回调实现对象为true–因此WCF只允许 一次客户端中有一个活动线程。因此,当WCF正在运行时 出站调用它将不允许对对象进行入站调用。 这会导致服务的初始死锁问题 当客户端的出站调用处于运行状态时,无法调度回调 进步。为了解决这个问题,我们使用“除非你告诉我其他”部分 根据上述规则。您可以通过注释回调来实现这一点 具有如下[CallbackBehavior]属性的实现类:


通过将其设置为Multiple或re-entrant(两者都有效,但re-entrant将强制一次执行一个线程,而Multiple将允许多个线程同时进入您的服务),您做得很好。我个人将其设置为“重新进入”,除非您需要多个线程同时进入您的服务


我刚刚在我的机器上写了一个测试用例,按照你的例子,它工作得很好。我认为你的IIS实现(在'11年)有一个bug。现在无法测试。

Thx用于链接。正如您在回调实现中看到的,我将并发模式设置为multiple(我还尝试了可重入)。这应该允许在具有多个线程的客户端上进行多个调用。ConcurrencyMode应该是唯一告诉WCF我将自己进行同步的语句。但这并不能像你在我的回答的博客帖子链接中看到的那样起作用。当ConcurrencyMode=ConcurrencyMode.Multiple应该足够了时,使用线程池是不符合逻辑的解决方案。我有一个类似的问题,它只在我在服务实现类上添加[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]时起作用,而在回调类上则不起作用。那么解决方案是什么呢?我知道这是几年前的事了,但对其他人来说仍然是有价值的信息。我原以为你的代码可以工作,而把它改成“可重入者”并不能解决问题。我很想复制你的测试,看看它是否是11年的bug,现在已经修复了。
[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)
          {
             ...
          }
      }
  }
[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]