2016-05-07 9 views
2

クライアントのサンプルコードでは非同期操作を待った後、いずれかの操作でハング:WCFクライアントはコンソールアプリケーション

var f = new DuplexChannelFactory<IService>(new Callback(), "NetTcpBinding_Name"); 
f.Credentials.ClientCertificate.Certificate = new X509Certificate2(certificateFile, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); 
f.Open(); 
IService s = f.CreateChannel(); 
Console.WriteLine("Lock status: " + s.IsUserLocked(id)); 
Task task = s.LockUserAsync(id); 
Console.Write("Sent, awaiting "); 
Console.WriteLine(task); 
await task; 
Console.WriteLine("Done"); 
Console.WriteLine("Lock status: " + s.IsUserLocked(id)); // never returns, timeout 

非同期メソッドに待った後LockUserAsync任意の同期方法は永遠にハングアップします。デバッガは、IsUserLockedが実際に2回目と呼ばれ、その復帰に達したことを示しています。

他のクライアントには影響しません。接続して同じことを開始から繰り返すことができます。

行動:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
    ConcurrencyMode = ConcurrencyMode.Multiple)] 

契約:

public async Task LockUserAsync(int id) 
{ 
    return; 
} 

public int IsUserLocked(int id) 
{ 
    return 0; 
} 

更新:

[ServiceContract(CallbackContract = typeof(IServiceCallback))] 
public interface IService 
{ 
    [OperationContract] 
    Task LockUserAsync(int userId); 
    [OperationContract] 
    int IsUserLocked(int id); 
} 

public interface IServiceCallback 
{ 
    [OperationContract] 
    void TestCallback(); // not used 
} 

どちらの方法がちょうどプレースホルダーです、それはありませんので、同期コンテキストコンソールアプリケーションです。私がawait.Wait()に置き換えた場合、それは機能します。 ConfigureAwait(false)は何も変更されません。

私は、継続が何とか直接Task.SetResultから呼び出されていることがわかりました。 ThreadPoolによって呼び出されないのはなぜですか?

+0

コンソールアプリケーションで 'main'メソッドを' async'にすることはできません。したがって、すべての非同期メソッドを待つためには 'Main'メソッドの中で' .GetAwaiter()。GetResult() 'を呼び出さなければなりません完了する。もちろん、これらのasyncメソッドの中では、Webサービスを呼び出すときに 'await'キーワードを使うことができます。 –

+0

@DarinDimitrovはい、私はすでにそうしています。そうでなければ、それはコンパイルされません。 – Vlad

答えて

2

ConsoleApplicationのようにSynchronizationContextが存在しない場合、TPLはTask.TrySetResultから直接継続を呼び出すことによって物事を最適化しようとします。私のコードはWCFを継続から呼び出すので、外部WCFコード内でデッドロックが発生します。

解決策は、それぞれawait s.WcfMethod();の後にawait Task.Yield();を置き、次の継続をThreadPoolから呼び出すことでした。

個人的に私はそれがWCFのバグだと思う:それはTask.Run()Task.SetResultを呼び出すかTaskCreationOptions.RunContinuationsAsynchronouslyTaskCompletionSourceに設定を渡す必要があります。

関連する問題