添加委托给事件 - 线程安全

时间:2010-08-19 13:41:22

标签: c#

可以同时从多个线程执行以下代码。

this._sequencer.Completed += OnActivityFinished;

将委托从多个线程添加到事件处理程序是否安全?

从多个线程中从事件处理程序中删除委托是否是线程安全的?

使这个线程安全的最简单和可维护的方法是什么?

3 个答案:

答案 0 :(得分:36)

如果您没有指定自己的事件添加/删除处理程序,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<EventHandler>(ref this.MyEvent, handler3, handler2);
    }
    while (myEvent != handler2);
}

删除处理程序看起来相同,但使用Delegate.Remove代替Delegate.Combine

请注意使用Interlocked.CompareExchange?这可以防止更新事件的后备字段和从中读取数据之间的竞争条件。因此,它是线程安全的。

答案 1 :(得分:7)

对于类似字段的事件,添加/删除处理程序是线程安全的。来自规范:

  

编译类似字段的事件时,编译器会自动创建存储以保存委托,并为添加或删除委托字段的事件处理程序的事件创建访问器。为了保证线程安全,在保持实例事件的包含对象的锁(第8.12节)或静态事件的类型对象(第7.6.10.6节)时完成添加或删除操作。

然而,对于C#3.0而言较少,在C#4.0编译器中使用Interlocked例程生成无锁实现(但规范仍然相同 - bug?)

在自定义实现中,没有人可以准确地说出......除了代码的作者:)

答案 2 :(得分:5)

说实话,这取决于事件的实施。

C#编译器生成的类字段事件是线程安全的,但如果它是自定义事件,谁知道呢?

请注意,在多线程应用程序中,您应该期望在添加/删除处理程序和事件触发之间存在竞争条件...例如,事件可能启动触发,然后您可以取消订阅,在取消订阅后仍会调用您的处理程序。