事件处理程序不是线程安全吗?

时间:2010-04-06 01:01:58

标签: c# .net multithreading events thread-safety

所以我一直在阅读,而不是直接用

调用事件
if (SomeEvent != null)
   SomeEvent(this, null);

我应该做

SomeEventHandler temp = SomeEvent;
if (temp != null)
    temp(this, null);

为什么会这样?第二个版本如何变得线程安全?什么是最佳做法?

4 个答案:

答案 0 :(得分:32)

IMO,其他答案错过了一个关键细节 - 委托(因此事件)是不可变的。这样做的重要性在于订阅或取消订阅事件处理程序不会简单地追加/删除到列表中 - 而是用一个额外的新列表替换列表(或少一个)项目。

由于引用是原子的,这意味着你可以这样做:

var handler = SomeEvent;

你现在有一个无法改变的刚性实例,即使在下一个皮秒中另一个线程取消订阅(导致实际事件字段变为null

所以你测试null并调用它,一切都很好。请注意,仍然在一个认为它在皮秒前取消订阅的对象引发的事件的混乱情景!

答案 1 :(得分:14)

事件在代表列表中确实是语法糖。当您调用该事件时,这实际上是在该列表上进行迭代并使用您传递的参数调用每个委托。

线程的问题在于它们可能通过订阅/取消订阅来添加或删除此集合中的项目。如果他们在迭代集合时执行此操作,则会导致问题(我认为会抛出异常)

目的是在迭代之前复制列表,这样就可以防止对列表进行更改。

注意:现在,即使您取消订阅也可以调用您的侦听器,因此您应确保在侦听器代码中处理此问题。

答案 2 :(得分:5)

最佳做法是第二种形式。原因是另一个线程可能在“SomeEvent”测试和调用之间为空或更改if

答案 3 :(得分:2)

Here是关于.NET事件和线程竞争条件的好文章。它涵盖了一些常见的场景,并在其中有一些很好的参考。

希望这有帮助。