27

이 질문에는 이미 답변이 있습니다.

그래서 나는 그 사건을 직접 읽지 않고 읽었습니다.

if (SomeEvent != null)
   SomeEvent(this, null);

나는해야한다.

SomeEventHandler temp = SomeEvent;
if (temp != null)
    temp(this, null);

이게 왜 그렇게? 두 번째 버전은 어떻게 스레드로부터 안전합니까? 가장 좋은 방법은 무엇입니까?


  • 여기에 잠정적 인 자격을 갖춘 대답을 읽으면 C #의 이벤트 처리가 밀접하게 결합되고 오류가 발생하기 쉽고 이해가 잘되지 않는다는 생각이 들게됩니다. - micahhoover

4 답변


14

이벤트는 실제로 대리자 목록에 대한 통사론적인 설탕입니다. 이벤트를 호출하면 실제로이 목록을 반복하고 전달한 매개 변수로 각 대리자를 호출합니다.

스레드의 문제점은 가입 / 탈퇴로이 컬렉션에 항목을 추가하거나 제거 할 수 있다는 것입니다. 그들이 컬렉션을 반복하는 동안이 작업을 수행하면 문제가 발생할 것입니다 (예외가 발생한다고 생각합니다)

목록은 반복하기 전에 목록을 복사하므로 목록 변경에 대해 보호됩니다.

참고 : 그러나 구독 취소 한 후에도 리스너를 호출 할 수 있으므로 리스너 코드에서 리스너를 처리해야합니다.


  • 실제로 컬렉션을 반복하는 동안 문제가 발생하지 않습니다. 여기에있는 대표단은 변경할 수 없습니다. 유일한 문제는 누군가가 가입했는지 실제로 확인하고 이벤트 핸들러를 실제로 호출하기 전에 분할 된 초입니다. 실행을 시작하면 배경 변경없이 현재 호출에 영향을 미칩니다. - Lasse Vågsæther Karlsen

30

IMO, 다른 답변은 하나의 주요 세부 사항을 놓치게됩니다. 대표자 (따라서 이벤트)는불변의. 이것의 중요성은 이벤트 핸들러를 구독하거나 구독 취소하는 것입니다하지 않는다간단히 목록에 추가 / 제거 - 오히려 그것을대체하다목록에 추가 항목이있는 새 목록이 표시됩니다.

참조가 원자 적이므로이 시점에서 참조하는 것은 다음을 의미합니다.

var handler = SomeEvent;

너는 이제엄격한다음 picosecond에서 다른 스레드가 구독 취소 한 경우에도 변경할 수없는 인스턴스입니다 (실제의될 이벤트 필드null).

그래서 당신은 null을 테스트하고 그것을 호출하고, 모든 것이 잘됩니다. 당연히 거기주의하십시오아직도사건의 혼란스러운 시나리오높인피코 세컨드를 구독 취소했다고 생각하는 객체에!


  • 이는 하나의 관심사를 다루지 만, 요청 된 모든 작업이 수행되도록 보장하기 위해 안전하게 대체 작업을 수행합니다. 즉, 두 스레드가 동시에 다른 대리자를 구독하려고 시도하는 경우 두 스레드 모두 최종적으로 구독하게 될지 또는 해당 구독 중 하나가 자동으로 실패 할 가능성이 있습니까? - binki
  • @ 빙키 예; 연동 교환 루프 (현대 컴파일러)를 사용하거나 동기화 된lock) 영역 (구형 컴파일러) - Marc Gravell

5

우수 사례는 두 번째 형식입니다. 그 이유는, 다른 thread가 null를 돌려 주거나 변경할 가능성이 있기 (위해) 때문에입니다.SomeEvent'if'테스트와 호출.


  • 두 번째 진술 문에서 왜 그럴 수 없습니까? - Daniel
  • 읽은 것은SomeEvent즉, 모든 일이 발생하거나 아무것도 발생하지 않습니다. 그러므로temp로컬이기 때문에 해당 스레드 외부에서 수정할 수 없습니다. - user7116
  • Ooopss/Thus/Also/ - user7116

2

이리스레드와 함께 .NET 이벤트 및 경쟁 조건에 대해 잘 기록합니다. 여기에는 몇 가지 일반적인 시나리오가 포함되어 있으며 이에 대한 좋은 참고 자료가 있습니다.

희망이 도움이됩니다.


  • 링크 주셔서 감사합니다. - Daniel

연결된 질문


관련된 질문

최근 질문