2015-11-09 26 views
4

好吧,我在我的智慧結束這件事情。我有一個WCF雙工服務。這裏是架構是如何工作的:WCF從另一個線程調用時,永不執行WCF雙工回調方法

  1. 客戶端打開到端點的連接,並提供一個回調實現
  2. 該服務採用這一請求並執行其他線程上的一些東西(可以1秒可能爲2分鐘,這是我沒有使用異步操作)
  3. 當處理完成後,它會調用客戶端

問題是,當服務調用回調,似乎什麼也沒有發生回調的原因。沒有錯誤,沒有任何東西。經過進一步調查,我發現服務器跟蹤中有一個例外:

The I/O operation has been aborted because of either a thread exit or an application request 

這種情況在嘗試執行回調後立即發生。

客戶端永遠不會收到響應或關閉。發生的一切是,因爲最初的請求是在一個不同於主線程的線程上進行的,所以它只是等待那個線程完成。

最奇怪的是,如果我嘗試在客戶端調用的操作中調用回調,而不進入另一個線程,則一切正常 - 回調被成功調用,這使我相信我有配置正確的服務,但有一個線程/死鎖問題。

這裏是我正在呼叫的服務:

SubmissionServiceClient client = CreateClientInstance(); 
     client.Open(); 
     Guid executionId = await client.SubmitAsync(submission); 
     submissionCompletionSource.Task.Wait(); //waits for the callback to be called (I omitted the extra wiring code for better readability) 
     client.Close(); 

private SubmissionServiceClient CreateClientInstance() 
{ 
    NetHttpBinding binding = new NetHttpBinding(); 
    binding.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always; 
    EndpointAddress endpointAddress = new EndpointAddress("ws://localhost:9080/SubmissionRouter"); 
    InstanceContext instanceContext = new InstanceContext(this); 
    SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress); 
    return submissionServiceClient; 
} 

這是回調操作:

public void SubmissionProcessed(SubmissionResultDto result) 
     { 
      submissionCompletionSource.TrySetResult(result); 
     } 

這是客戶端調用服務操作:

public Guid Submit(SubmissionDto submission, ISubmissionCallback callback) 
     { 
      ExecutionDto execution = new ExecutionDto() 
      { 
       Id = Guid.NewGuid(), 
       Submission = submission 
      }; 
      RequestExecution(execution); //Queues the execution of the operation 
      submissions.Add(execution.Id, callback); 

      return execution.Id; 
     } 

這就是服務調用客戶端回調的地方(這個方法在另一個線程f上執行)黑山初始請求上所做的一個):

   ISubmissionCallback callback = submissions[submissionResult.ExecutionId]; 
        callback.SubmissionProcessed(submissionResult); 

服務行爲:

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

正如你可以在提交時看到,服務店鋪的回調在字典中,用一個id配對,它稍後用來檢索回調並調用它。我相信它失敗的原因是因爲我試圖在另一個線程上做到這一點。

編輯: 我加入了Ping操作的服務,調用另一個線程,掛起3秒鐘,然後調用客戶端上的傍功能。

public void Ping() 
     { 
      var callback = OperationContext.Current.GetCallbackChannel<ISubmissionCallback>(); 
      Task.Run(() => 
      { 
       System.Threading.Thread.Sleep(3000); 
       callback.Pong(); 
      }); 
     } 

客戶端: 類節目 { 靜態無效的主要(字符串[]參數) { NetHttpBinding結合=新NetHttpBinding(); binding.WebSocketSettings。TransportUsage = WebSocketTransportUsage.Always; EndpointAddress endpointAddress = new EndpointAddress(「ws:// localhost:9080/SubmissionRouter」); InstanceContext instanceContext = new InstanceContext(new Callback()); SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress);

 submissionServiceClient.Ping(); 

     Console.Read(); 
    } 

    public void SubmissionProcessed(SubmissionResultDto result) 
    { 
     throw new NotImplementedException(); 
    } 
    class Callback : ISubmissionServiceCallback 
    { 
     public void Pong() 
     { 
      Console.WriteLine("Pong!"); 
     } 

     public void SubmissionProcessed(SubmissionResultDto result) 
     { 

     } 
    } 
} 

這實際上工作成功。我設法在客戶端收到我的答案。我現在正式完全失去了。

+0

作爲一個測試,你可以嘗試在你的線程上回調一個非常基本的方法,例如一個沒有像'Ping'這樣的參數並且看看會發生什麼?在建立連接後嘗試限制頻道上的任何其他通信/操作,以便簡化重新創建。 – wal

+0

你是否保持對服務器端連接的引用? (不只是回調參考) – wal

+0

你是否用'submissionCompletionSource.Task.Wait();'阻塞UI線程?此外,爲什麼當你有'等待'這行時,你會叫'Wait',難道你不願意'await submissionCompletionSource.Task'嗎? –

回答

3

如果您阻止導致死鎖的submissionCompletionSource.Task.Wait();的UI線程。 WCF回調發生默認UI線程上,你可以通過使用CallbackBehaviorAttribute

[CallbackBehaviorAttribute(UseSynchronizationContext=false)] 
class Callback : ISubmissionServiceCallback 
{ 
    public void Pong() 
    { 
     Console.WriteLine("Pong!"); 
    } 

    public void SubmissionProcessed(SubmissionResultDto result) 
    { 

    } 
} 

或不堵UI線程改變行爲。

SubmissionServiceClient client = CreateClientInstance(); 
client.Open(); 
Guid executionId = await client.SubmitAsync(submission); 
await submissionCompletionSource.Task; //awaits for the callback to be called without blocking the UI. 
client.Close(); 
+0

我已經嘗試做UseSynchronizationContext = false,並沒有幫助。我會盡快嘗試你的第二個建議。 – Phoenix

+0

你先生真棒!建議2號工作。現在,如果我只能明白爲什麼。 – Phoenix

+0

@Phoenix它發生是因爲你同步等待異步操作,[這是一個關於該問題的深度博客](http://blog.stephencleary.com/2012/07/dont-block-on-async-code的.html) –