【问题标题】:Add delegate to event - thread safety向事件添加委托 - 线程安全
【发布时间】:2011-04-01 03:27:29
【问题描述】:

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

this._sequencer.Completed += OnActivityFinished;

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

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

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

【问题讨论】:

    标签: c#


    【解决方案1】:

    如果您不指定自己的事件添加/删除处理程序,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);
    }
    

    还有一个 remove 处理程序,看起来相同,但使用 Delegate.Remove 而不是 Delegate.Combine

    注意到Interlocked.CompareExchange 的使用了吗?这可以防止更新事件的支持字段和读取它之间的竞争条件。因此,它是线程安全的。

    【讨论】:

    • 一个小说明-这是.NET 4中的实现,在此之前它使用lock(this)(另见desco的答案)
    • @ohadsc 我刚刚创建了一个事件并针对 .net 2 进行编译,它仍然创建了 timwi 定义的联锁代码
    • @Simon 你是对的,它是编译器功能,而不是运行时功能。我应该说“这是 C# 4 编译器中的实现”
    【解决方案2】:

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

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

    但是对于 C# 3.0 和更低版本来说确实如此,在 C# 4.0 中编译器使用互锁例程生成无锁实现(但规范保持不变 - 错误?)

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

    【讨论】:

    • 我认为规范已经更新,但更新版本尚未发布。
    • 也许,我指的是此处可用的版本(发布日期 4/19/2010)-microsoft.com/downloads/…
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-16
      • 1970-01-01
      相关资源
      最近更新 更多