【问题标题】:Async call to WCF client blocks subsequent synchronous calls对 WCF 客户端的异步调用会阻止后续同步调用
【发布时间】:2016-06-14 17:43:30
【问题描述】:

在客户端上调用生成的异步方法时,我发现 WCF 出现问题...如果我等待异步方法,然后在同一客户端上调用非异步方法,则阻塞方法永远不会返回.

这是问题的简单再现:

[ServiceContract]
public interface IService1
{
    [OperationContract] void DoWork();
}

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Service1 : IService1
{
    public void DoWork()
    {
    }
}

这里没有什么复杂的。公开单个方法的简单 WCF 服务(同步实现)。

class Program
{
    static void Main(string[] args)
    {
        var svc = new ServiceHost(typeof(Service1));
        svc.Open();
        RunClient().Wait();    // This is a console app. There is no SynchronizationContext to confuse things.
        svc.Close();
    }

    static async Task RunClient()
    {
        var client = new ServiceReference1.Service1Client();
        client.DoWork();
        Console.WriteLine("Work Done");

        await client.DoWorkAsync();
        Console.WriteLine("Async Work Done");

        Console.WriteLine("About to block until operation timeout...");
        client.DoWork();
        Console.WriteLine("You'll never get here.");

    }
}

请注意,这不是常见的情况,即有人阻止消息泵线程或忘记调用ConfigureAwait(false)。这是一个控制台应用,添加ConfigureAwait 对行为没有影响。

奇怪的是,帮助是做什么的:

await Task.Delay(1);   // Magical healing worker thread context switch

在再次调用同步 WCF 方法之前。因此,在从异步方法调用恢复后,WCF 似乎以某种方式留下了一些线程本地上下文,然后将其清除。

知道是什么原因造成的吗? 死锁/非死锁情况的调用堆栈揭示了 WCF 客户端操作的一些差异:

良好的堆栈:

System.ServiceModel.dll!System.ServiceModel.Channels.TransportDuplexSessionChannel.Receive(System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.TransportDuplexSessionChannel.TryReceive(System.TimeSpan timeout, out System.ServiceModel.Channels.Message message)    System.ServiceModel.dll!System.ServiceModel.Dispatcher.DuplexChannelBinder.Request(System.ServiceModel.Channels.Message message, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message)
mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type)

坏栈:

mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)
mscorlib.dll!System.Threading.WaitHandle.WaitOne(System.TimeSpan timeout, bool exitContext)
System.ServiceModel.Internals.dll!System.Runtime.TimeoutHelper.WaitOne(System.Threading.WaitHandle waitHandle, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Dispatcher.DuplexChannelBinder.SyncDuplexRequest.WaitForReply(System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation)
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message)
mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type)

在异步调用完成后,DuplexChannelBinder WCF 中的某处似乎假设有另一个线程从通道读取消息。

有什么想法吗?显然,我对在我的代码中添加治疗 Task.Delay 语句的想法并不感到兴奋......

干杯, 标记

【问题讨论】:

  • 所以,我认为问题不是线程本地存储上下文问题。我认为这与由于 IO 完成而从 WCF 堆栈的上下文中调用的异步延续有关。当异步继续返回时(即当我们等待其他东西时),会运行更多的 WCF 框架代码来改变客户端通道的状态,并“修复”问题。
  • 有什么消息吗?找到了一些真正的解决方案?我有“相同的”(?)问题......
  • 对不起,如果这是愚蠢的......但是否可以保证 WCF 库创建一个具有独立执行线程的主机?在同一个线程中托管客户端和主机似乎从一开始就有问题。你能把宿主对象推到一个单独的线程中吗?
  • 客户端和服务器是否在不同的进程中无关紧要,同样的问题仍然存在。上面的代码只是一个最小的复制案例。

标签: c# wcf asynchronous async-await deadlock


【解决方案1】:

我在使用同一个客户端调用多个操作时遇到了类似的问题。 使用同一个对象不能同时调用多个方法,看起来像在wcf方法调用内部实现了一些锁定。

所以在 1 个调用未完成之前,您不能使用相同的对象再次调用 wcf 服务。

【讨论】:

  • 不,这与我的问题无关。
猜你喜欢
  • 2012-10-03
  • 2013-11-21
  • 2011-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 2016-04-24
  • 2013-03-10
相关资源
最近更新 更多