C#事件同步吗?

Ale*_*ird 96 .net c# events delegates

这个问题分为两部分:

  1. 是否提高事件阻塞线程,或者它异步启动事件处理器的执行和线程便在同一时间继续吗?

  2. 单独的事件处理器(订阅事件)运行同步一个接一个,或者是他们还不能保证别人不会在同一时间运行的异步运行?

Dan*_*rth 70

这是一般性答案,反映了默认行为:

  1. 是的,如果订阅事件的方法不是异步的,它会阻塞线程.
  2. 它们一个接一个地执行.这有另一个转折:如果一个事件处理程序抛出异常,则不会执行尚未执行的事件处理程序.

话虽如此,提供事件的每个类都可以选择异步实现其事件.IDesign提供了一个称为EventsHelper简化此类的类.

[注意]此链接要求您提供下载EventsHelper类的电子邮件地址.(我不以任何方式加入)

  • 我读过一些论坛帖子,其中有两个与第一点相矛盾,但没有提供适当的理由。我不怀疑你的答案,(它与我迄今为止所经历的相符)关于第一点有任何官方文档吗?我需要确定这一点,但我很难找到有关此事的官方信息。 (2认同)

KFL*_*KFL 27

回答你的问题:

  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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Foo的实现:

在此输入图像描述

请注意,有一个字段 OnCall和一个事件 OnCall.该领域OnCall显然是支持财产.它只是一个Func<int, string>,没有什么花哨的.

现在有趣的部分是:

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • 以及如何OnCall调用Do()

如何实施订阅和取消订阅?

这是add_OnCallCIL中的缩写实现.有趣的是它用于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
Run Code Online (Sandbox Code Playgroud)

同样,Delegate.Remove用于remove_OnCall.

如何调用事件?

要调用OnCallDo(),它只是加载ARG后调用最终级联委托:

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

订阅者如何订阅活动?

最后,Main并非OnCall令人惊讶的是,通过调用实例add_OnCall上的方法来订阅事件Foo.

  • 做得好!!我问这个问题已经很久了.如果你能把词汇放在最直接回答我的两部分问题的顶部(即"#1答案是否定的;#2答案是否定的")那么我将把它作为正式答案.我打赌你的帖子作为回答我原来问题的所有部分,但由于我不再使用C#(而其他googlers可能是这些概念的新手),这就是为什么我要求提供明显的答案的措辞. (2认同)
  • @KFL,目前还不清楚,我正要留下与亚历克斯相同的评论。一个简单的“是的,它们是同步的”会有帮助 (2认同)

Vla*_*mir 14

订阅该事件的代理按照添加顺序同步调用.如果其中一个委托抛出异常,则不会调用以下代理.

由于事件是使用多播委托定义的,因此您可以使用编写自己的触发机制

Delegate.GetInvocationList();
Run Code Online (Sandbox Code Playgroud)

并异步调用委托;


And*_*lov 12

事件只是代表的数组.只要委托调用是同步的,事件也是同步的.


Jam*_*iec 7

通常,事件是同步的.但是有一些例外,例如如果为null 则System.Timers.Timer.ElapsedThreadPool线程上引发事件SyncronisingObject.

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