2012-04-17 3 views
2

비동기식 콜백을위한 다중 스레드 환경에서 이벤트 처리기를 올바르게 추가/제거하는 방법에 대한 질문이 있습니다.다중 스레드 응용 프로그램에 대한 이벤트 처리기 추가 및 제거

비 관리 코드에서 콜백을 전달하는 ProxyDLL에서 비동기 콜백을받는 MyCore 클래스가 있습니다. 나는 이벤트를 구독하는 형태 (관리 형)를 가지고있다.

이벤트 연결/제거에 대한 올바른 접근 방법은 무엇입니까? MulticastDelegate에 _invocationcount가 있음을 알았습니다. 그것은 무엇입니까? 콜백 호출이 진행 중일 때 이벤트 콜백이 완료 될 때까지 이벤트의 내부 로직이 이벤트로부터 분리되는 것을 차단합니까? 해당 _ 포유류에 _invocationcount가 있습니까? 이벤트 (일반적으로)에서 탈퇴하는 것이 굳건한가요?

class Form1 
{ 
    EventHandler m_OnResponse; 
    Int32 m_SomeValue; 
    Form1() 
    { 
    m_OnResponse = new EventHandler(OnResponseImpl); 
    m_MyCore.SetCallBackOnLogOn(m_OnResponse); 
    } 
    ~Form1() 
    { 
    m_MyCore.ReleaseCallBackOnLogOn(m_OnResponse); 
    } 
    private OnResponseImpl(object sender, EventArgs e) 
    { 
    Thread.Sleep(60*1000); 

    m_SomeValue = 1;    // <<-- How to/Who guarantees that Form1 obj is still 
           // alive. May be callback was invoked earlier and 
           // we just slept too long 

    if (!this.IsDisposed) 
    { 
     invokeOnFormThread(DoOnResponseImpl, sender, e); 
    } 
    } 
} 

class MyCore 
{ 
    private event EventHandler OnLogOn; 
    public void SetCallBackOnLogOn(EventHandler fn) 
    { 
    // lock (OnLogOn) 
    { 
     OnLogOn += fn; 
    } 
    } 
    ReleaseCallBackOnLogOn(EventHandler fn) 
    { 
    // lock (OnLogOn) 
    { 
     OnLogOn -= fn; 
    } 
    } 
    public void DoDispatchOnLogOn() 
    { 
    // lock (OnLogOn) 
    { 
     if (OnLogOn != null) 
     { 
     OnLogOn(this, null); 
     } 
    } 
    } 
} 

답변

3

기본 이벤트 추가 및 제거 작업은 already thread-safe이다. 대부분의 경우이 부분에 대해 걱정할 필요가 없습니다. 걱정할 필요가있는 멀티 캐스트 대리자가 호출됩니다.

public void DoDispatchOnLogOn() 
{ 
    EventHander local; 
    lock (this) 
    { 
    local = OnLogOn; 
    } 
    if (local != null) 
    { 
    local(this, null); 
    } 
} 

내가 여기서 한 것은 OnLogOn 대리인 체인을 보유 할 로컬 변수를 만드는 것이 었습니다. 멀티 캐스트 위임자의 불변성을 이용하여 널 (null) 및 호출 시퀀스에 대한 스레드 안전성 검사를 수행 할 수 있습니다. lockOnLogon의 "신선한"읽기를 보장하는 데에만 사용되며 "부실한"읽기를 허용하지 않으려면 엄격하게 선택 사항입니다.

업데이트 : 나는 ReleaseCallBackOnLogOn 수신 거부 대리자를 종료 할 때 콜백이 호출되지 않았는지 확인해야

.

대부분의 경우 내가 가지고있는 코드는 구독 취소 된 이벤트 핸들러를 실행하지 않습니다. 이벤트 처리기를 제거하고 최근에 제거 된 이벤트 처리기가 실행될 수있는 이벤트를 발생시키는 사이에는 약간의 경쟁이 있습니다. 콜백 호출이 완전히 완료 될 때까지

나는 내 클래스의 인스턴스가 아직 살아 있는지 확인해야합니다.

대리인은 대상 메서드가 포함 된 클래스 인스턴스에 대한 참조를 보유합니다. 이렇게하면 인스턴스가 루트가되어 가비지 수집 대상이되지 않습니다. 이 점에 대해 걱정할 필요가 없습니다.

마무리자인 ~Form1은 이벤트 처리기를 제거하기에 적합하지 않습니다. 위임자는 대상 메서드가 포함 된 인스턴스에 대한 참조를 보유하므로 대부분의 경우 종료자가 호출되지 않고 이벤트 처리기가 이벤트에서 제거되지 않습니다.

+0

감사합니다. 좋은 지적입니다. 그리고 안전에 관해서 - 당신은 이벤트로부터 청취자를 구독 취소하는 것이 막히는 호출이라고 말하고 싶습니까? – adspx5

+0

@ adspx5 : C# 4.0 이상에서는 Interlocked.CompareExchange를 사용하여 가입 및 가입 취소를 수행합니다. C# 3.0 이하에서는'lock (this)'을 사용합니다. C# 3.0 이하는 잠금이 현재 다른 사람에 의해 유지되는 경우 차단됩니다. –

+0

그것은 내가 이해하려고하는 것이 아닙니다. 이해했지만 호출 목록 변경은 걱정하지 않아도됩니다. 나는 대의원의 일생을 걱정한다. ReleaseCallBackOnLogOn이 탈퇴 대리인을 종료 할 때 콜백이 호출되지 않도록해야합니다. 10 초 동안 내 대리자가 잠자기 상태가되고 그 후에 MyForm :: m_Somevalue가 변경됩니다.콜백 호출이 완전히 완료 될 때까지 클래스 인스턴스가 아직 살아 있는지 확인해야합니다. – adspx5

관련 문제