WCF回调死锁
我遇到了一个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
[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)]