后续的WCF调用导致阻塞原因不明,最终导致TimeoutException?

时间:2016-04-13 01:43:34

标签: c# wcf

我不确定为什么会如此。但我确定如果使用异步调用,则不会阻止。

这里的场景是我有2个WCF方法调用,第一个将触发对客户端的一些回调调用(使用CallbackContract)。第二个只是一个普通的WCF方法调用(即使是一个根本没有代码的空方法)。

方法内容并不重要,这里只是某种伪代码:

public void FirstMethod(){
    //some logic here...
    //here I use some Callback method to client side
    clientCallbackInterface.SomeMethod();//commenting this out won't 
    //cause any blocking.
}

public void SecondMethod(){
    //this is even empty
}

//call the 2 methods synchronously in a sequence
client.FirstMethod();
client.SecondMethod();

不调用SecondMethod,它运行得很好。如果使用异步调用,它也运行得很好。或者如果我注释掉调用(使用客户端回调接口),它也会运行得很好。

在抛出异常TimeoutException时,它显示方法SecondMethod实际上已完成并处于响应客户端的阶段。

ServiceBehavior有InstanceContextMode PerSessionConcurrencyMode Multiple

我希望此处有人对此有所了解并理解这个问题背后的原因。

更新

  • 我只是通过将ConcurrencyMode设置为Single来尝试新事物,而且运行得很好。所以我想了解更多如何使ConcurrencyMode Multiple {/ 1}}正常运行?

更新

我真的很困惑这里有什么问题,事实上有一些旧的代码甚至没有使用CallbackBehavior,它只能与ConcurrencyMode的{​​{1}}一起使用。虽然我的代码需要Multiple,但在第二次执行2个方法时失败了。这是我可以发布的最小代码,我已经尝试过了,方法内容并不重要,它可能只是空的:

CallbackBehavior

上面的//the service interface [ServiceContract(CallbackContract = typeof(IMyClient), SessionMode = SessionMode.Allowed)] public interface IMyService { bool MyMethod(); } //the client callback interface public interface IMyClient { [OperationContract(IsOneWay = true)] void OnSomething(SomeEventArgs e); } //the service class [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)] [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, UseSynchronizationContext = false)] public class MyService : IMyService { static Dictionary<ClientInfo, IMyClient> clients; static Dictionary<ClientInfo, IMyClient> Clients { get { if (clients == null) clients = new Dictionary<ClientInfo, IMyClient>(); return clients; } } static void raiseEvents(Action<IMyClient> raiser, params Guid[] toClients) { if (raiser == null) throw new ArgumentNullException("raiser cannot be null."); System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(o => { lock (clients) { //ClientInfo is just some class holding some info about //the client such as its ClientGuid Func<KeyValuePair<ClientInfo, IMyClient>, bool> filter = c => toClients.Length == 0 || toClients.Any(e => e == c.Key.ClientGuid); foreach (var client in Clients.Where(filter).Select(e => e.Value)) { raiser(client); } } })); } public bool MyMethod(){ //do nothing before trying to trigger some callback to the client raiseEvents(e => e.OnSomething(new SomeEventArgs())); return true; } } 方法实际上是我遵循的旧代码(正如我所提到的那样工作得很好),就像之前它看起来更简单(并且也不起作用):

raiseEvents

旧代码和我编写的代码之间的一个可能的区别在于配置文件,但我不确定哪个可能导致此问题。事实上,我已尽可能多地克隆配置(关于static void raiseEvents(Action<IMyClient> raiser, params Guid[] toClients) { if (raiser == null) throw new ArgumentNullException("raiser cannot be null."); Func<KeyValuePair<ClientInfo, IPosClient>, bool> filter = c => toClients.Length == 0 || toClients.Any(e => e == c.Key.ClientGuid); foreach (var client in Clients.Where(filter).Select(e => e.Value)) { Task.Run(() => raiser(client)); } } )。

如最初所述,这里涉及两种方法。但是这次我只有一个方法,如代码中所示。它第一次调用OK时,下次调用它将冻结UI(就像有一些死锁一样)。当您拥有客户端代理类(由“添加服务引用”向导自动生成)时,调用它很简单:

<behaviors>

事实上,我可以使用//this is put in some Execute method of some Command (in WPF) myServiceClient.MyMethod(); 的{​​{1}}版本解决此问题,或者只是将该调用放在一个帖子中,但旧代码不需要这样做而且我可以使用async。我真的很好奇为什么它第一次起作用,但下次一直保持冻结(直到MyMethod被抛出)。

1 个答案:

答案 0 :(得分:1)

默认情况下,WCF将在当前的SynchronizationContext上执行回调。这意味着当你从例如WPF或WinForms应用程序的UI线程调用WCF服务时 - 回调也将在UI线程上执行。但是这个线程已被您的服务调用阻止,因此您对服务和服务回调客户端的调用将会死锁。首先 - 不要从UI线程调用远程服务,无论如何从用户体验的角度来看都是坏的(你的界面会在等待调用结果时冻结)。但是,如果您仍然这样做,至少通过使用CallbackBehavior属性告诉WCF不使用当前的同步上下文进行回调:

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant, UseSynchronizationContext=false)]
class Callback : IClientCallback
{

}