C# 事件是同步的吗? [英] Are C# events synchronous?

查看:32
本文介绍了C# 事件是同步的吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题有两个部分:

  1. 引发一个事件会阻塞线程,还是异步开始执行 EventHandler 并且线程同时继续运行?

  2. 各个 EventHandlers(订阅事件)是一个接一个地同步运行,还是异步运行而不能保证其他事件处理程序不会同时运行?p>

解决方案

是的,它们是同步的.

回答您的问题:

  1. 如果事件处理程序都是同步实现的,则引发事件确实会阻塞线程.
  2. 事件处理程序按照订阅事件的顺序依次执行.

我也很好奇event的内部机制及其相关操作.所以我写了一个简单的程序并使用 ildasm 来探索它的实现.

简短的回答是

  • 订阅或调用事件不涉及异步操作.
  • 事件是使用相同委托类型的支持委托字段实现的
  • 订阅是通过

    请注意,有一个 field OnCall 和一个 event OnCall.字段 OnCall 显然是支持属性.而且它只是一个 Func,这里没什么特别的.

    现在有趣的部分是:

    • add_OnCall(Func)
    • remove_OnCall(Func)
    • 以及如何在 Do()
    • 中调用 OnCall

    订阅和取消订阅是如何实现的?

    这是 CIL 中的缩写 add_OnCall 实现.有趣的部分是它使用 Delegate.Combine 连接两个委托.

    .method public hidebysig specialname instance voidadd_OnCall(class [mscorlib]System.Func`2'value') cil 管理{//....locals init (class [mscorlib]System.Func`2 V_0,class [mscorlib]System.Func`2V_1,class [mscorlib]System.Func`2V_2)IL_0000:ldarg.0IL_0001:ldfld 类 [mscorlib]System.Func`2ConsoleApp1.Foo::OnCall//...IL_000b:调用类 [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,类 [mscorlib]System.Delegate)//...}//方法 Foo::add_OnCall 结束

    同样,Delegate.Remove 用于remove_OnCall.

    如何调用事件?

    要在 Do() 中调用 OnCall,它只需在加载 arg 后调用最终连接的委托:

    IL_0026: callvirt 实例 !1 class [mscorlib]System.Func`2::Invoke(!0)

    订阅者究竟如何订阅事件?

    最后,在 Main 中,毫不奇怪,订阅 OnCall 事件是通过调用 Foo 上的 add_OnCall 方法完成的 实例.

    There are two parts to this question:

    1. Does raising an event block the thread, or does it start execution of EventHandlers asynchronously and the thread goes continues on at the same time?

    2. Are the individual EventHandlers (subscribed to the event) run synchronously one after another, or are they run asynchronously with no guarantee that others aren't running at the same time?

    解决方案

    Yes, they are synchronous.

    To answer your questions:

    1. Raising an event does block the thread if the event handlers are all implemented synchronously.
    2. The event handlers are executed sequentially, one after another, in the order they are subscribed to the event.

    I too was curious about the internal mechanism of event and its related operations. So I wrote a simple program and used ildasm to poke around its implementation.

    The short answer is

    • there's no asynchronous operation involved in subscribing or invoking the events.
    • event is implemented with a backing delegate field of the same delegate type
    • subscribing is done with Delegate.Combine()
    • unsubscribing is done with Delegate.Remove()
    • Invoking is done by simply invoking the final combined delegate

    Here's what I did. The program I used:

    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();
        }
    }
    

    Here's Foo's implementation:

    Note that there is a field OnCall and an event OnCall. The field OnCall is obviously the backing property. And it's merely a Func<int, string>, nothing fancy here.

    Now the interesting parts are:

    • add_OnCall(Func<int, string>)
    • remove_OnCall(Func<int, string>)
    • and how OnCall is invoked in Do()

    How is Subscribing and Unsubscribing Implemented?

    Here's the abbreviated add_OnCall implementation in CIL. The interesting part is it uses Delegate.Combine to concatenate two delegates.

    .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
    

    Likewise, Delegate.Remove is used in remove_OnCall.

    How is an event invoked?

    To invoke OnCall in Do(), it simply calls the final concatenated delegate after loading the arg:

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

    How exactly does a subscriber subscribe to an event?

    And finally, in Main, not suprisingly, subscribing to the OnCall event is done by calling add_OnCall method on the Foo instance.

    这篇关于C# 事件是同步的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆