2016-04-13 8 views
1

私はそれがなぜそうかもしれないのか分かりません。しかし、私は非同期呼び出しを使用する場合は、ブロックされませんと確信しています。その後のWCF呼び出しにより、原因不明のブロックが発生し、最終的にTimeoutExceptionが発生しますか?

ここでのシナリオでは、2つのWCFメソッド呼び出しがあります。最初の呼び出しでは、クライアントへのコールバック呼び出しが発生します(CallbackContractを使用)。 2つ目は、通常のWCFメソッド呼び出し(コードを一切持たない空のメソッドでさえ)です。

メソッドの内容は重要ではありません、ここでは擬似コードのほんのいくつかの種類である:SecondMethodを呼び出さずに

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(); 

、それはうまく動作します。非同期呼び出しを使用している場合は、正常に動作します。また、(クライアントコールバックインターフェイスを使用して)コールをコメントアウトすると、うまく動作します。

例外TimeoutExceptionがスローされた時点で、方法SecondMethodが実際に実行され、クライアントに応答する段階にあることが示されます。

ServiceBehaviorは、InstanceContextModePerSessionConcurrencyModeMultipleです。

私はここで誰かがこれを経験し、この問題の背後にある原因を理解してくれることを願っています。

UPDATE

  • 私はちょうど代わりSingleからConcurrencyModeを設定することで、新しいものを試してみたし、それもうまく実行します。ですから、ConcurrencyModeMultipleの場合、それをうまく動作させる方法をもっと知りたいですか?

UPDATE

私はここで間違っているかについて、本当に困惑している、実際にも、CallbackBehaviorを使用して、それは単にMultipleConcurrencyModeと正常に動作しないいくつかの古いコードがあります。私のコードはCallbackBehaviorを必要とし、2つの方法を実行する2回目に失敗しました。ここで私は投稿できる最小のコードは、私はそれを試してみたされ、メソッドの内容は本当に重要ではありません、それだけで空にすることができます:

//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方法は、(私は古いコードを踏襲するものです古いコードと私は書いている1間

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)); 
     }     
    } 

一つの可能​​な差が設定している:私はそれが)動作しません。また、(このような非常に簡単になりますし、以前のように、)だけで正常に動作している述べたようにこの問題につながる可能性はあまりありません。実際に私はできるだけ多くの構成を複製しようとしました(約<behaviors>)。

最初に説明したように、ここには2つの方法が含まれます。しかし、今回はコードのように1つのメソッドしかありません。最初はOKと呼ばれ、次回はUIをフリーズします(デッドロックのように)。あなたは(サービスの追加リファレンス・ウィザードによって自動生成された)クライアントプロキシクラスを持っているときにそれを呼び出すと、ただ単純です:

実際に
//this is put in some Execute method of some Command (in WPF) 
myServiceClient.MyMethod(); 

私は仕事-の周りにすることができ、この問題MyMethodasyncバージョンを使用しますか、単純にその呼び出しをスレッドに入れますが、古いコードではそれを行う必要はなく、なぜ初めて動作するのか不思議ですが、次回は凍結(TimeoutExceptionまで)を続けます。

+0

クライアントアプリケーションの性質は何ですか? WPF/WinFormsまたはいくつかのWebページ?あなたはどのようにクライアント側でチャネル/プロキシを取得するのですか? –

+0

@IgorLabutinクライアントアプリケーションはWPFアプリケーションであり、サービスのメソッドにアクセス/呼び出しするためにクライアントのクラスを生成するために、サービス参照の追加ウィザードを使用します。ご回答いただきありがとうございます。この問題を解決するためのご意見がありましたら幸いです。 – Hopeless

+3

こちら(最後はこちら):http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,b891610a-6b78-4b54-b9a6-4ec81c82b7c0.aspxほとんどの場合、WPFアプリケーションのUIスレッドから呼び出され、コールバックはUIスレッドにもディスパッチされます。そのコールバックオブジェクトを[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant、UseSynchronizationContext = false)]を使用してデコレートしてみてください。 – Evk

答えて

1

既定では、WCFは現在のSynchronizationContextでコールバックを実行します。つまり、UIスレッドからWPFやWinFormsアプリケーションのWCFサービスを呼び出すと、UIスレッドでもコールバックが実行されます。しかし、このスレッドはすでにサービス呼び出しによってブロックされているため、サービスとサービスのコールバック呼び出しはデッドロックになります。まず、UIのスレッドからリモートサービスを呼び出さないでください。それは、ユーザーエクスペリエンスの観点からは悪いことです(呼び出しの結果を待つ間にインターフェイスがフリーズします)。しかし、それでもやっても、CallbackBehavior属性を使用して、コールバックに現在のシノニム化コンテキストを使用しないようWCFに指示してください。

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

} 
関連する問題