Are C# events synchronous?

这个问题分为两部分:

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

  2. 个别 EventHandler(订阅事件)是一个接一个同步运行,还是异步运行,不能保证其他同时运行?

32908 次浏览

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

这是一个普遍的答案,反映了默认行为:

  1. 是的,如果订阅事件的方法不是异步的,它会阻塞线程。
  2. 他们一个接一个被处决。这里还有另一个问题: 如果一个事件处理程序抛出异常,那么尚未执行的事件处理程序将不会被执行。

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

[Note] this link requires you to provide an e-mail address to download EventsHelper class. (I am not affiliated in any way)

通常,事件是同步的。但是也有一些例外,例如如果 SyncronisingObject为空,则在 ThreadPool线程上引发 System.Timers.Timer.Elapsed事件。

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

C # 中的事件同步运行(在这两种情况下) ,只要不手动启动第二个线程。

Events are synchronous. This is why the event lifecycle works the way it does. Inits happen before loads, loads happen before renders etc.

If no handler is specified for an event, the cycle just blazes through. If more than one handler is specified, they will be called in order and one can't continue until the other is completely finished.

即使是异步调用也在一定程度上是同步的。在开始完成之前,是不可能叫结束的。

The delegates subscribed to the event are invoked synchronously in the order they were added. If one of the delegates throws an exception, the ones following will not be called.

因为事件是用多播委托定义的,所以您可以使用

Delegate.GetInvocationList();

and invoking the delegates asynchronously;

是的,它们是同步的。

回答你的问题:

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

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.

简单来说就是

  • 在订阅或调用事件时不涉及异步操作。
  • 事件使用同一委托类型的后备委托字段实现
  • Delegate.Combine()完成订阅
  • unsubscribing is done with 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 的实现:

enter image description here

请注意,有一个 场地 OnCall和一个 事件 OnCall。字段 OnCall显然是后备属性。它只是一个 Func<int, string>,没什么特别的。

Now the interesting parts are:

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

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

下面是 CIL 中简化的 add_OnCall实现。有趣的是它使用 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.Removeremove_OnCall中使用。

如何调用事件?

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

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

How exactly does a subscriber subscribe to an event?

最后,在 Main中,通过调用 Foo实例上的 add_OnCall方法来订阅 OnCall事件,这并不奇怪。