10

私は少なくとも 1 週間、この問題に頭を悩ませてきました (新しいことも学びました - WCF は主要な PITA です)。

ここに私の問題があります: 私のアプリには、ある時点でクライアント全体を永遠にフリーズさせるシナリオがあります (クライアントとサーバーの両方が制御された環境にあるため、タイムアウトを無効にしたため)。デッドロックはまったく同じ呼び出しで発生します。これは、それに先行する要求のバーストが原因であると推測されます。

クライアントでデッドロック スタック トレースを調べると、次のようになります。

[In a sleep, wait, or join] 
WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + 0x26 bytes 
mscorlib.dll!System.Threading.SynchronizationContext.InvokeWaitMethodHelper(System.Threading.SynchronizationContext syncContext, System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) + 0x1c bytes   
[Native to Managed Transition]  
[Managed to Native Transition]  
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + 0x2b bytes 
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext) + 0x2d bytes    
mscorlib.dll!System.Threading.WaitHandle.WaitOne() + 0x10 bytes 
System.Runtime.DurableInstancing.dll!System.Runtime.TimeoutHelper.WaitOne(System.Threading.WaitHandle waitHandle, System.TimeSpan timeout) + 0x7c bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.OverlappedContext.WaitForSyncOperation(System.TimeSpan timeout, ref object holder) + 0x40 bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.PipeConnection.WaitForSyncRead(System.TimeSpan timeout, bool traceExceptionsAsErrors) + 0x38 bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.PipeConnection.Read(byte[] buffer, int offset, int size, System.TimeSpan timeout) + 0xef bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.DelegatingConnection.Read(byte[] buffer, int offset, int size, System.TimeSpan timeout) + 0x21 bytes   
System.ServiceModel.dll!System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(System.ServiceModel.Channels.StreamUpgradeInitiator upgradeInitiator, ref System.ServiceModel.Channels.IConnection connection, System.ServiceModel.Channels.ClientFramingDecoder decoder, System.ServiceModel.IDefaultCommunicationTimeouts defaultTimeouts, ref System.Runtime.TimeoutHelper timeoutHelper) + 0xb3 bytes  
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(System.ServiceModel.Channels.IConnection connection, System.ArraySegment<byte> preamble, ref System.Runtime.TimeoutHelper timeoutHelper) + 0x155 bytes  
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(System.ServiceModel.Channels.IConnection connection, ref System.Runtime.TimeoutHelper timeoutHelper) + 0x25 bytes  
System.ServiceModel.dll!System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(System.TimeSpan timeout) + 0xe2 bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(System.TimeSpan timeout) + 0x37 bytes 
System.ServiceModel.dll!System.ServiceModel.Channels.CommunicationObject.Open(System.TimeSpan timeout) + 0x13f bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.OnOpen(System.TimeSpan timeout) + 0x52 bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.CommunicationObject.Open(System.TimeSpan timeout) + 0x13f bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(System.ServiceModel.Channels.ServiceChannel channel, System.TimeSpan timeout) + 0x12 bytes  
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(System.TimeSpan timeout, System.ServiceModel.Channels.ServiceChannel.CallOnceManager cascade) + 0x10c bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannel.Call(string action, bool oneway, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation, object[] ins, object[] outs, System.TimeSpan timeout) + 0x18b bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(System.Runtime.Remoting.Messaging.IMethodCallMessage methodCall, System.ServiceModel.Dispatcher.ProxyOperationRuntime operation) + 0x59 bytes    
System.ServiceModel.dll!System.ServiceModel.Channels.ServiceChannelProxy.Invoke(System.Runtime.Remoting.Messaging.IMessage message) + 0x65 bytes    
mscorlib.dll!System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(ref System.Runtime.Remoting.Proxies.MessageData msgData, int type) + 0xee bytes    
MyService.dll!MyService.Controller.CallMethod() + 0x9 bytes

バースト呼び出しシーケンスが疑われる理由は、呼び出しが行われる前に 60 秒のスリープを挿入すると、デッドロックが発生しないためです。

この問題を回避する方法について何か提案はありますか?

PS私は名前付きパイプを使用しています。

編集:

クライアント側での WCF サービスへの呼び出しは、GUI スレッドで発生します。デッドロックの原因となっている GUI スレッドにアクセスしようとしていると (コールスタックから) 推測するのは正しいでしょうか?

編集:

クライアント側チャネル ファクトリの初期化:

var binding = new NetNamedPipeBinding
    {
        OpenTimeout = TimeSpan.MaxValue,
        CloseTimeout = TimeSpan.MaxValue,
        SendTimeout = TimeSpan.MaxValue,
        ReceiveTimeout = TimeSpan.MaxValue,
        ReaderQuotas = { MaxStringContentLength = Int32.MaxValue, MaxArrayLength = Int32.MaxValue },
        MaxBufferPoolSize = Int32.MaxValue,
        MaxBufferSize = Int32.MaxValue,
        MaxReceivedMessageSize = Int32.MaxValue
    };
CustomBinding pipeBinding = new CustomBinding(binding);
pipeBinding.Elements.Find<NamedPipeTransportBindingElement>().ConnectionPoolSettings.IdleTimeout = TimeSpan.FromDays(24);
channelFactory = new ChannelFactory<ITestsModule>(pipeBinding,
    new EndpointAddress(string.Format("net.pipe://localhost/app_{0}/TestsModule", ProcessId)));

サーバー側ホストの初期化:

var host = new ServiceHost(m_testModule, new Uri[] { new Uri(string.Format("net.pipe://localhost/app_{0}", Process.GetCurrentProcess().Id)) });
ServiceThrottlingBehavior throttle = host.Description.Behaviors.Find<ServiceThrottlingBehavior>();

if (throttle == null)
{
    throttle = new ServiceThrottlingBehavior();
    throttle.MaxConcurrentCalls = 500;
    throttle.MaxConcurrentSessions = 200;
    throttle.MaxConcurrentInstances = 100;
    host.Description.Behaviors.Add(throttle);
}

ThreadPool.SetMinThreads(1000, 1000);

var binding = new NetNamedPipeBinding
    {
        OpenTimeout = TimeSpan.MaxValue,
        CloseTimeout = TimeSpan.MaxValue,
        SendTimeout = TimeSpan.MaxValue,
        ReceiveTimeout = TimeSpan.MaxValue,
        ReaderQuotas = { MaxStringContentLength = Int32.MaxValue, MaxArrayLength = Int32.MaxValue },
        MaxBufferPoolSize = Int32.MaxValue,
        MaxBufferSize = Int32.MaxValue,
        MaxReceivedMessageSize = Int32.MaxValue
    };

CustomBinding pipeBinding = new CustomBinding(binding);
pipeBinding.Elements.Find<NamedPipeTransportBindingElement>().ConnectionPoolSettings.IdleTimeout = TimeSpan.FromDays(24);

host.AddServiceEndpoint(typeof(ITestsModule), pipeBinding, "TestsModule");

サービス クラスの動作:

[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.Single,
    ConcurrencyMode = ConcurrencyMode.Multiple,
    UseSynchronizationContext = false,
    IncludeExceptionDetailInFaults = true
)]
4

1 に答える 1

0

まず、サーバー側で何をロックしているか知っていますか? ロックの競合は WCF インターフェイスからのみ発生していますか? または、サーバーが他のコンポーネント/クラスからもロックしていますか? これは最も重要な質問であり、WCF とは関係ありません。

さて、これは言った、問題を絞り込むのを助けるためにこれを試してください:

オプション 1: クライアント側のタイムアウト - Int32.MaxValue に設定せず、10 秒に設定し、タイムアウト時にクライアント側の再試行を実装します。

オプション 2:

ServiceThrottlingBehavior ThrottleBehavior = new ServiceThrottlingBehavior();
ThrottleBehavior.MaxConcurrentSessions = 4;
ThrottleBehavior.MaxConcurrentCalls = 4;
ThrottleBehavior.MaxConcurrentInstances = 4;

ServiceHost Host = ...
Host.Description.Behaviors.Add(ThrottleBehavior);

オプション 2 が役立つ場合は、ストレス テストを行います (オプション 1 と同じようにする必要があります)。MaxConcurrentXXX が大きな数に設定されている場合は、スレッド数の増加にも注意してください。

お役に立てれば

于 2013-12-24T20:00:31.177 に答える