簡體   English   中英

防止WCF雙工回調服務出現死鎖問題

[英]Prevent deadlock issue with WCF duplex callback service

我有一個自托管的wcf雙工回調服務的問題。 我收到帶有消息的InvalidOperationException

此操作將死鎖,因為在當前消息完成處理之前無法接收回復。 如果要允許無序消息處理,請在CallbackBehaviorAttribute上指定Remediate的ConcurrencyMode或Multiple。

這是我的服務行為:

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

這是我的服務合同:

 [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientCallback))]

[ServiceContract]
public interface IClientToService
{
    [OperationContract(IsOneWay = false)]
    LVSSStatus GetLvssStatus();

    [OperationContract(IsOneWay = true)]
    void PickSpecimen(long trackingNumber, int destCode);

    [OperationContract(IsOneWay = true)]
    void CancelCurrentPickTransaction();
}

這是我的回調界面:

public interface ILvssClientCallback
{
    [OperationContract(IsOneWay = true)]
    void SendClientCallback(LvssCallbackMessage callbackMessage);

    [OperationContract(IsOneWay = false)]
    List<SpecimenTemplateDescriptor> GetTemplateDescriptorList(DrawerLayout drawerLayout);

    [OperationContract(IsOneWay = false)]
    SpecimenTemplate SelectSpecimenTemplate(string templateName, int version);

    [OperationContract]
    void SpecimenStoredInContainer(string containerID, bool isValidRackID, int rackRow, int rackCol, int deckRow, int deckCol,
     int drawerRow, int drawerCol, long trackingNumber, RobotErrors robotError);

    [OperationContract]
    void LvssRobotStatusChange(LVSSStatus status);
}

我知道在客戶端上調用回調操作時會導致InvalidOperationException ,該服務已被鎖定以處理當前操作。 因此,發生了死鎖。

我嘗試將ConcurrencyMode更改為多個,並將UseSynchronizationContext更改為false。

我仍然看到我的服務有兩個問題:

第一:當快速調用GetLvssStatus()時(通過快速單擊UI按鈕),以下服務操作會凍結我的客戶端wpf應用程序。 此方法不是單向的,並且會將服務中的枚舉類型同步返回給客戶端。

    [OperationContract(IsOneWay = false)]
    LVSSStatus GetLvssStatus();

*在這種情況下,是什么導致我的wpf應用程序凍結? *我可以做些什么來防止應用凍結? 如果我使用backgroundworker線程作為異步調用,應用程序不會凍結。 我真的需要這種方法同步工作。

第二:當我將回調方法LvssRobotStatusChange分配給IsOneWay = true ,我得到一個ObjectDisposedException:無法訪問已處置的對象。 對象名稱: 'System.ServiceModel.Channels.ServiceChannel'

    [OperationContract(IsOneWay = true)]
    void LvssRobotStatusChange(LVSSStatus status);

*導致此ObjectDisposedException的原因是什么? *在這種情況下可以省略IsOneWay分配嗎? 在這種情況下省略IsOneWay允許回調完成而沒有任何異常。

*這些問題可能是缺乏線程安全代碼的結果嗎?
*如果是這樣,使ConcurrencyMode.Multiple服務行為線程安全的最佳做法是什么?

對這些問題的任何幫助都非常感謝。

* FIRST EDIT關於創建雙工通道的更多信息。 我的wpf視圖模型創建了一個代理對象,負責處理我的頻道的創建。 到目前為止,在客戶端的新線程上設置我的通道的任何嘗試都會在服務嘗試使用回調對象時導致ObjectDisposedException。

*第二次編輯我相信如果我可以通過void方法獲得操作合同來設置IsOneWay = true,我的服務應該可以工作。 對於可重入的並發性,主通道線程應該允許這些方法通過而不管任何鎖定。
這是我的回調界面:

public interface ILvssClientCallback
{
    [OperationContract(IsOneWay = true)]
    void SendClientCallback(LvssCallbackMessage callbackMessage);

    [OperationContract]
    List<SpecimenTemplateDescriptor> GetTemplateDescriptorList(DrawerLayout drawerLayout);

    [OperationContract]
    SpecimenTemplate SelectSpecimenTemplate(string templateName, int version);

    [OperationContract(IsOneWay = true)]
    void SpecimenStoredInContainer(string containerID, bool isValidRackID, int rackRow, int rackCol, int deckRow, int deckCol,
     int drawerRow, int drawerCol, long trackingNumber, RobotErrors robotError);

    [OperationContract(IsOneWay = true)]
    void LvssRobotStatusChange(LVSSStatus status);
}

當我將方法LvssRobotStatuschange操作契約設置為IsOneWay = true時,我的緩存回調通道會拋出CommunicationObjectAbortedException。 出於某種原因,我的回調屬性正在中止。

***什么可能導致回調通道中止?

我之前遇到過這個問題, 這個鏈接應該有幫助,它討論了在應用程序主線程以外的線程上創建通道。

我遇到的問題:

CallBackHandlingMethod()
{
    requestToService();    // deadlock message.    
}

出路:

CallBackHandlingMethod()
{
    Task.Factory.StartNew(()=>
    {
        requestToService();
    });
}

我有類似的問題,我只是通過添加解決

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]

到我的回調實現。

[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServiceCallbackHandler : IServiceCallback
{
 ...
}

當使用UseSynchronizationContext = true並且CallbackBehavior.ConcurrencyMode的值不是Multiple ,您在從對服務的調用內進行回調時會創建死鎖。 調用堆棧如下所示:

  • 客戶: btDoSomething_ClickService.DoSomething();
    • 服務器: DoSomethingCallback.ReportUpdate();
      • 客戶端:在IO回調上, CallbackSynchronizationContext.Send(delegate { Callback.ReportUpdate(); })

CallbackSynchronizationContext.Send的調用掛起,因為它引用執行btDoSomething_Click的線程。 有許多方法可以擺脫這種循環:

  1. 避免使服務同步(通過應用[OperationContract(IsOneWay = true)]
    (這會導致客戶端在請求發送到服務器后立即釋放UI線程,而不是等待Service.DoSomething的回復)。

  2. 使回調不需要UI線程(通過應用[CallbackBehavior(UseSynchronizationContext = false)][CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    (在任何一種情況下,對回調的調用都將來自線程池線程。如果你需要編組回UI線程,你仍然可以使用Synchronizationcontext.Post

  3. 將客戶端調用更新為async (將服務合同從[OperationContract] void DoSomething();更改為[OperationContract] void DoSomething();更改為[OperationContract] Task DoSomethingAsync();


TL; DR:在非OneWay操作中與[CallbackBehavior(UseSynchronizationContext = true)]進行回調的組合將導致客戶端的同步上下文出現死鎖。 如果需要在同步上下文中使用回調,請將操作協定更改為使用異步:

舊:

[ServiceContract(CallbackContract = typeof(IControllerServiceCallback))]
public interface IControllerService
{

    [OperationContract]
    void OpenDrawer();
}

新:

[ServiceContract(CallbackContract = typeof(IControllerServiceCallback))]
public interface IControllerService
{
    [OperationContract]
    Task OpenDrawerAsync();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM