添加委托给事件 – 线程安全
可以同时从多个线程执行以下代码。
this._sequencer.Completed += OnActivityFinished;
将委托从多个线程添加到事件处理程序是否安全?
从多个线程中删除委托给事件处理程序的线程安全吗?
使这个线程安全的最简单和可维护的方法是什么?
如果您没有指定自己的事件添加/删除处理程序,C#编译器将生成此添加处理程序 (由.NET Reflector重建):
public void add_MyEvent(EventHandler value) { EventHandler handler2; EventHandler myEvent = this.MyEvent; do { handler2 = myEvent; EventHandler handler3 = (EventHandler) Delegate.Combine(handler2, value); myEvent = Interlocked.CompareExchange(ref this.MyEvent, handler3, handler2); } while (myEvent != handler2); }
和一个看起来相同但使用Delegate.Remove
而不是Delegate.Combine
的删除处理程序 。
注意使用Interlocked.CompareExchange
? 这可以防止更新事件的后备字段和从中读取数据之间的竞争条件。 因此,它是线程安全的。
对于类似字段的事件,添加/删除处理程序是线程安全的。 来自规格:
编译类似字段的事件时,编译器会自动创建存储以保存委托,并为事件创建访问器,以便向委托字段添加或删除事件处理程序。 为了是线程安全的,在保持实例事件的包含对象的锁(第8.12节)或静态事件的类型对象(第7.6.10.6节)的同时完成添加或删除操作。
但是对于C#3.0来说确实如此,在C#4.0编译器中使用Interlocked例程生成无锁实现(但规范保持不变 – bug?)
在自定义实现中没有人可以确切地说…除了代码的作者:)
说实话,这取决于事件的实施。
C#编译器生成的类字段事件是线程安全的,但如果它是自定义事件,谁知道呢?
请注意,在multithreading应用程序中,您应该期望在添加/删除处理程序和事件触发之间存在竞争条件…例如,事件可能会开始触发,然后您可以取消订阅,并且您的处理程序仍会在调用之后调用取消订阅。