C# 事件是同步的吗? [英] Are C# events synchronous?
问题描述
这个问题有两个部分:
引发一个事件会阻塞线程,还是异步开始执行 EventHandler 并且线程同时继续运行?
各个 EventHandlers(订阅事件)是一个接一个地同步运行,还是异步运行而不能保证其他事件处理程序不会同时运行?p>
是的,它们是同步的.
回答您的问题:
- 如果事件处理程序都是同步实现的,则引发事件确实会阻塞线程.
- 事件处理程序按照订阅事件的顺序依次执行.
我也很好奇event
的内部机制及其相关操作.所以我写了一个简单的程序并使用 ildasm
来探索它的实现.
简短的回答是
- 订阅或调用事件不涉及异步操作.
- 事件是使用相同委托类型的支持委托字段实现的
- 订阅是通过
请注意,有一个 field
OnCall
和一个 eventOnCall
.字段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`2 V_1,class [mscorlib]System.Func`2 V_2)IL_0000:ldarg.0IL_0001:ldfld 类 [mscorlib]System.Func`2 ConsoleApp1.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:
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?
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:
- Raising an event does block the thread if the event handlers are all implemented synchronously.
- 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 usedildasm
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 eventOnCall
. The fieldOnCall
is obviously the backing property. And it's merely aFunc<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 inDo()
How is Subscribing and Unsubscribing Implemented?
Here's the abbreviated
add_OnCall
implementation in CIL. The interesting part is it usesDelegate.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 inremove_OnCall
.How is an event invoked?
To invoke
OnCall
inDo()
, 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 theOnCall
event is done by callingadd_OnCall
method on theFoo
instance.这篇关于C# 事件是同步的吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!