2011-10-31 88 views
3

我遇到了一個調用客戶端回調方法的WCF服務的問題。首先,服務:WCF回調死鎖

[ServiceContract(
SessionMode = SessionMode.Required, 
CallbackContract = typeof(IMarketObserver))] 
public interface IServer 
{ 
    ... 
    [OperationContract] 
    [FaultContractAttribute(typeof(WCFFaultDetail))] 
    bool NotifyOnMarket(EnumMarkets marketID); 
    ... 
} 

服務實現:

[ServiceBehavior(
InstanceContextMode = InstanceContextMode.Single, 
ConcurrencyMode = ConcurrencyMode.Multiple, 
UseSynchronizationContext = false)] 
public sealed class Platform : IServer 
{ 
... 
public bool NotifyOnMarket(EnumMarkets marketID) 
{ 
    try 
    { 
     IMarketObserver callback = OperationContext.Current.GetCallbackChannel<IMarketObserver>(); 
     if (subscribers.Contains(callback) == false) 
     { 
      subscribers.Add(callback); 
     } 
    } 
    catch 
    { 
     return false; 
    } 
    //This call may cause a call to the callback method SendMarketData()!! 
    callSomeMethod(); 
    return exchangeProxy.IsMarketIDValid(); 
} 

回調合同:

public interface IMarketObserver 
{ 
    [OperationContract(IsOneWay = true)] 
    void SendMarketData(MarketData marketData); 
} 

這個回調的客戶端實現是:

[CallbackBehavior(
    ConcurrencyMode = ConcurrencyMode.Multiple, 
    UseSynchronizationContext = false)] 
public class MarketBase : 
    IServerCallback 
{ 
    protected IService serviceProxy; 
    public void SendMarketData(MarketData marketData) 
    { 
     //Do something 
    } 
    private void NotifyOnMarkets() 
    { 
     foreach (EnumMarkets item in observedMarkets) 
     { 
      try 
      { 
       bool res = serviceProxy.NotifyOnMarket(item); 
      } 
      catch (Exception e) 
      { 
      ... 
      } 
     } 
    } 

出現的問題當c用foreach循環來分配NotifyOnMarkets()。

如果observingMarkets列表中只有一個項目,那麼只有一個調用服務的NotifyOnMarket()方法,並且一切工作正常。

但是,如果observedmarkets包含多個項目,那麼NotifyOnMarket()會以高頻率向服務器多次調用。

服務器上NotifyOnMarket()的實現調用一個方法,該方法又會調用回調方法SendMarketData(我評論過這個事實)。

在痕跡中,我可以看到serviceProxy.NotifyOnMarket(item);不會在第二個項目上返回,則會發生超時。

在服務器端,正確處理對NotifyOnMarket()的多重調用,並退出該方法。但如上所述,布爾結果不會顯示在客戶端(超時)。我們可以看到在服務器端調用了回調(這是單向的,所以沒有迴應會被返回),但是在客戶端沒有反應,因爲沒有調用回調實現。

我的結論是發生了某種類型的死鎖,這可能是由於客戶端實例鎖定自身,所以服務器無法調用回調方法。

將實例上下文類與也正在執行服務調用的類分開會更好嗎?如果是這樣,爲什麼?

謝謝你的建議,

克林斯曼

回答

4

對於你的問題:爲什麼它需要在不同的線程? 閱讀的WCF Duplex Messaging結束:

WCF通過強制規定說 增加了更多的複雜性到混合「除非你告訴我,否則我將只允許一個線程在同一時間 到我控制對象」 。你可以看到這個單身人士服務 ,默認情況下一次只允許一次呼叫。這同樣也是 回調實現對象的真實情況 - 所以WCF一次只允許在客戶端有一個活動線程 。因此,雖然WCF執行 出站呼叫,但它不允許入站呼叫進入該對象。 這會導致死鎖的初始問題,即在客戶的外撥呼叫處於 進度中時,無法分派服務的 回撥。爲了解決這個問題,我們使用「除非你另外告訴我」上述規則的 部分。您可以通過使用[CallbackBehavior]屬性類似這樣的註釋回調 實現類做到這一點:

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)] 
+0

Thx爲鏈接。正如你可以看到我的回調實現,我將ConcurrencyMode設置爲多個(我也試過Reentrant)。這應該允許多線程客戶端上的多個調用。 ConcurrencyMode應該是告訴WCF我會爲自己做同步的唯一語句。但是,這並不能完成這項工作,因爲您可以在我的答案的博客文章鏈接中看到。當ConcurrencyMode = ConcurrencyMode.Multiple應該足夠時,使用ThreadPool是不合邏輯的解決方案。 – Juergen

+0

我有一個類似的問題,它只有當我在服務實現類上添加[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]時才起作用,當我將它放在回調類上時它不起作用。 – Jonathan

+0

那麼解決方案是什麼?我知道這是幾年前,但它仍然是其他人有價值的信息。我原以爲你的代碼會起作用,而將其改爲「重入」並不能解決問題。我很想複製你的測試,看看它是不是現在已修復的'11版本中的錯誤。 –

0

你將其設置爲多個或重入(包括工作做的是正確的,但再進入者將一次強制執行1個線程,而多個進程將允許多個線程一次進入您的服務)。我個人將其設置爲「重入」,除非您需要多個線程一次進入您的服務。

我剛剛在我的機器上寫了一個測試用例,按照你的例子,它工作得很好。我認爲你的IIS實現(回到'11)有一個錯誤。現在不可能測試。