2013-08-28 3 views
4

.NET 4.0 이후 자동 생성 된 추가/제거 이벤트 핸들러는 스레드 안전 (herehere)입니다. 따라서 노출 된 이벤트에 리스너를 등록한 클라이언트는 레이스없이 여러 스레드에서 동시에 수행 할 수 있습니다.다중 스레드 환경에서 이벤트 올리기

하지만 스레드를 안전하게 실행하려면 어떻게해야합니까? 권장되는 방법은 다음 (here) 것 같다 그러나

public event EventHandler MyEvent; 
protected void OnMyEvent(EventArgs e) 
{ 
    EventHandler myEvent = MyEvent; 
    if (myEvent != null) 
    { 
     myEvent(this, e); 
    } 
} 

, 나는 더 이상이 올바른 생각하지 않는 .NET 메모리 모델 (예를 들어, MSDN 잡지 2012-122013-01) 읽은 것을 가지고.

public event EventHandler MyEvent; 
protected void OnMyEvent(EventArgs e) 
{ 
    // JIT removed the local variable and introduced two memory reads instead. 
    if (MyEvent != null) 
    { 
     // A race condition may cause the following line to throw a NullReferenceException. 
     MyEvent(this, e); 
    } 
} 

지역 변수를 제거하고는 않기 때문에 반복 메모리 읽기 사용이 법적 : 내 관심사는 메모리가 컴파일러에 의해 도입 될 수있다 그래서 위의 코드는이 같은 것으로 JIT - 테드가 될 수 읽입니다 단일 스레드 환경에서 실행될 경우 메서드의 동작을 변경하지 않습니다. 이것은 ECMA 사양 (ECMA-335: I.12.6.4)입니다. 이해하기 쉬운 예제는 MSDN 매거진의 2013-01 호에서도 제공됩니다.

여기에 뭔가가 있습니까? 그렇지 않은 경우 해결 방법을 알려주십시오.

+0

지적 해 주셔서 감사합니다. 나는 SO와 인터넷을 수색했지만 분명히 그 자리를 놓쳤다. 내 두 센트를 추가하려면 실제로 당신이 말하는 질문에 대해 잘못된 대답이 선택되었다고 생각합니다. 올바른 하나가이 것 같습니다 - http://stackoverflow.com/a/2365885/385737. ECMA 사양은 반복적 인 메모리 읽기로 지역 변수를 대체 할 수있게합니다. – Tomas

+0

내 자신의 질문을 두 번 더 추가 - [this] (http://stackoverflow.com/questions/16162745/event-raising-and-read-introduction-in-net-4-5) 및 특히 [this] (http://stackoverflow.com/questions/14799876/read-introduction-in-c-sharp-how-to-protect-against-it) 위대한 답은 Eric Lippert입니다. – Tomas

답변

2

당신은 다중 스레드 환경에서 첫 번째 조각이 올바른지 확인하기 위해 전용 라인을 추가해야합니다 :

public event EventHandler MyEvent; 
protected void OnMyEvent(EventArgs e) 
{ 
    EventHandler myEvent = MyEvent; 
    Thread.MemoryBarrier(); 
    if (myEvent != null) 
    { 
     myEvent(this, e); 
    } 
} 

메모리 장벽 읽기 순서를 거부하고 컴파일러와 CPU 모두 기록합니다. 이것이 휘발성 읽기/쓰기가 구현되는 방식입니다. 더 많은 것을 읽을 수 about memory barrier here.

+0

제 생각에는 메모리 장벽은 메모리 읽기 및 쓰기 (예 : 컴파일러 또는 프로세서 캐시)를 재정렬하는 것을 금지하는 울타리입니다. 그러나 이것은 메모리 장벽이 메모리 읽기가 강제/제거되는 방식에도 영향을 미친다는 것을 의미하지는 않습니다. 귀하의 요점을 뒷받침하는 참고 자료를 조언하거나 알려주십시오. – Tomas

+0

컴파일러 및 프로세서는이 코드가 얼마나 많은 양의 실제 쓰기 및 읽기를 생성하는지 보증하지 않습니다. 통화 간에도 다를 수 있습니다. 그리고 예, 기억 장벽은 그러한 행동에 영향을 미치지 않습니다. 즉, 이벤트 위임에 대한 MB 참조가 없으면 메모리, 캐시, 등록 위치에서 가져올 수 있습니다. 메모리 및 캐시는 많은 스레드에 의해 수정됩니다. 그래서 NRE가 가능합니다. 메모리 배리어 강제 컴파일러는 스레드 스택에 델리게이트에 대한 참조를 저장합니다. 다른 스레드는이를 수정할 수 없습니다. 이렇게하면 확인하고 전화를 거는 것이 안전합니다. –

+0

Jury가 나와 함께 해 주셔서 감사하지만 값을 힙에서 스레드 스택으로 이동하는 것은 나에게 의미가 없습니다. 그러면 값이 어떻게 힙으로 돌아갈 것입니까? 게다가 나는 ECMA 표준이 그런 작업을 언급하지 않는다고 믿는다. – Tomas

관련 문제