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

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

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

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



답변

질문에 답하려면 :

  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인스턴스에서 메서드를 호출하여 수행됩니다 .


답변

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

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

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

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


답변

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

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

Delegate.GetInvocationList();

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


답변

이벤트는 델리게이트의 배열 일뿐입니다. 대리자 호출이 동기식이면 이벤트도 동기식입니다.


답변

일반적으로 이벤트는 동기식입니다. 그러나 다음과 같은 경우 스레드 System.Timers.Timer.Elapsed에서 이벤트가 발생하는 것과 같은 몇 가지 예외가 있습니다.ThreadPoolSyncronisingObject null 인 가 있습니다.

문서 : http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx


답변

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


답변

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

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

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