C # 이벤트는 동기식입니까?


104

이 질문에는 두 부분이 있습니다.

  1. 이벤트를 발생 시키면 스레드가 차단 됩니까? 아니면 EventHandler의 실행을 비동기 적으로 시작하고 스레드가 동시에 계속됩니까?

  2. 있습니까 개별 EventHandlers 동 기적으로 다른 후 하나를 실행 (이벤트에 가입)이, 또는 다른 사람들이 동시에 실행되지 않는다는 보장은 없습니다 비동기 실행입니까?

답변:


37

질문에 답하려면 :

  1. 이벤트 처리기가 모두 동 기적으로 구현 된 경우 이벤트를 발생 시키면 스레드가 차단됩니다.
  2. 이벤트 핸들러는 이벤트에 등록 된 순서대로 순차적으로 실행됩니다.

나도 내부 메커니즘 event과 관련 작업 에 대해 궁금 했습니다. 그래서 저는 간단한 프로그램을 작성하고 ildasm그 구현을 찌르는 데 사용 했습니다.

짧은 대답은

  • 이벤트 구독 또는 호출과 관련된 비동기 작업이 없습니다.
  • 이벤트는 동일한 대리자 유형의 지원 대리자 필드로 구현됩니다.
  • 구독은 Delegate.Combine()
  • 구독 취소는 Delegate.Remove()
  • 호출은 최종 결합 된 델리게이트를 호출하여 수행됩니다.

여기 내가 한 일이 있습니다. 내가 사용한 프로그램 :

public class Foo
{
    // cool, it can return a value! which value it returns if there're multiple 
    // subscribers? answer (by trying): the last subscriber.
    public event Func<int, string> OnCall;
    private int val = 1;

    public void Do()
    {
        if (OnCall != null) 
        {
            var res = OnCall(val++);
            Console.WriteLine($"publisher got back a {res}");
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub2: I've got a {i}");
            return "sub2";
        };

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub1: I've got a {i}");
            return "sub1";
        };

        foo.Do();
        foo.Do();
    }
}

다음은 Foo의 구현입니다.

여기에 이미지 설명 입력

있음을 유의 필드 OnCall이벤트 OnCall . 필드 OnCall는 분명히 뒷받침 속성입니다. 그리고 그것은 단지 Func<int, string>.

이제 흥미로운 부분은 다음과 같습니다.

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • 그리고 어떻게 OnCall호출 되는지Do()

구독 및 구독 취소는 어떻게 구현됩니까?

다음 add_OnCall은 CIL 의 축약 된 구현입니다. 흥미로운 부분은 Delegate.Combine두 대리자를 연결 하는 데 사용 된다는 것입니다.

.method public hidebysig specialname instance void 
        add_OnCall(class [mscorlib]System.Func`2<int32,string> 'value') cil managed
{
  // ...
  .locals init (class [mscorlib]System.Func`2<int32,string> V_0,
           class [mscorlib]System.Func`2<int32,string> V_1,
           class [mscorlib]System.Func`2<int32,string> V_2)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall
  // ...
  IL_000b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  // ...
} // end of method Foo::add_OnCall

마찬가지로 Delegate.Remove사용된다 remove_OnCall.

이벤트는 어떻게 호출됩니까?

호출하기 OnCallDo(), 단순히 인수를로드 한 후 최종 연결된 대리자를 호출합니다 :

IL_0026:  callvirt   instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)

구독자가 이벤트를 정확히 어떻게 구독합니까?

마지막으로 Main놀랍게도에서 OnCall이벤트 구독 add_OnCallFoo인스턴스에서 메서드를 호출하여 수행됩니다 .


3
잘 했어!! 이 질문을 한 지 너무 오래되었습니다. 두 부분의 질문에 직접 답하는 말을 맨 위에 올릴 수 있다면 (즉, "# ​​1 대답은 아니요, # 2 대답은 아니요") 공식 대답으로 만들겠습니다. 나는 귀하의 게시물을 내 원래 질문에 답하기 위해 모든 조각으로 걸고 있지만 더 이상 C #을 사용하지 않기 때문에 (다른 Google 사용자는 이러한 개념에 익숙하지 않을 수 있습니다) 그래서 대답을 분명하게 만드는 말을 요청하고 있습니다.
Alexander Bird

@AlexanderBird에게 감사드립니다. 답변을 맨 위에 올리기 위해 편집했습니다.
KFL

@KFL, 여전히 불분명합니다. Alex와 같은 댓글을 남기려고했습니다. 간단한 "예, 동기식입니다."라고하면 도움이 될 것입니다
johnny 1

71

이것은 일반적인 대답이며 기본 동작을 반영합니다.

  1. 예, 이벤트를 구독하는 메서드가 비동기가 아닌 경우 스레드를 차단합니다.
  2. 그들은 차례로 실행됩니다. 여기에는 또 다른 문제가 있습니다. 하나의 이벤트 핸들러가 예외를 발생 시키면 아직 실행되지 않은 이벤트 핸들러는 실행되지 않습니다.

하지만 이벤트를 제공하는 모든 클래스는 이벤트를 비동기 적으로 구현하도록 선택할 수 있습니다. IDesign은이EventsHelper 를 단순화 하는 클래스를 제공합니다 .

[참고] 이 링크는 EventsHelper 클래스를 다운로드하기 위해 이메일 주소를 제공해야합니다. (저는 어떤 식 으로든 제휴하지 않습니다)


나는 몇 개의 포럼 게시물을 읽었으며, 두 개는 첫 번째 요점과 모순되는 반면 적절한 이유를 제공하지 않았습니다. 나는 당신의 대답을 의심하지 않습니다. (지금까지 경험 한 것과 일치합니다) 첫 번째 요점에 대한 공식 문서가 있습니까? 나는 이것에 대해 확신 할 필요가 있지만, 정확한 문제에 대해 공식적인 것을 찾는 데 어려움이 있습니다.
Adam LS

@ AdamL.S. 이벤트가 어떻게 호출되는지의 문제입니다. 따라서 이벤트를 제공하는 클래스에 따라 다릅니다.
Daniel Hilgarth

14

이벤트를 구독 한 대리자는 추가 된 순서대로 동 기적으로 호출됩니다. 대리자 중 하나가 예외를 throw하면 다음 대리자가 호출 되지 않습니다 .

이벤트는 멀티 캐스트 델리게이트로 정의되므로 다음을 사용하여 자신 만의 발사 메커니즘을 작성할 수 있습니다.

Delegate.GetInvocationList();

및 델리게이트를 비동기 적으로 호출합니다.




3

C #의 이벤트는 두 번째 스레드를 수동으로 시작하지 않는 한 동 기적으로 (두 경우 모두) 실행됩니다.


비동기 이벤트 핸들러를 사용하는 것은 어떻습니까? 그런 다음 다른 스레드에서 실행됩니까? "Async all the way"에 대해 들었지만 비동기 이벤트 핸들러에 자체 스레드가있는 것 같습니다. 이해가 안 돼요 : / 제발 계몽 해 주 시겠어요?
Winger Sendon

3

이벤트는 동기식입니다. 이것이 이벤트 라이프 사이클이 작동하는 방식으로 작동하는 이유입니다. 초기화는로드 전에 발생하고로드는 렌더링 전에 발생합니다.

이벤트에 대한 핸들러가 지정되지 않으면주기가 계속됩니다. 둘 이상의 핸들러가 지정되면 순서대로 호출되고 다른 하나가 완전히 완료 될 때까지 하나를 계속할 수 없습니다.

비동기 호출조차 어느 정도 동 기적입니다. 시작이 완료되기 전에 끝을 부르는 것은 불가능합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.