c# - 在事件分派(dispatch)之前检查 null ...线程安全吗?

标签 c# multithreading events

有些事情让我感到困惑,但从来没有造成任何问题......推荐的调度事件的方式如下:

public event EventHandler SomeEvent;
...
{
    ....
    if(SomeEvent!=null)SomeEvent();
}

在多线程环境中,此代码如何保证另一个线程不会在检查 null 和调用事件之间更改 SomeEvent 的调用列表?

最佳答案

正如您所指出的,在多个线程可以同时访问 SomeEvent 的情况下,一个线程可以检查 SomeEvent 是否为 null 并确定它是否为 null。就在这样做之后,另一个线程可以从 SomeEvent 中删除最后注册的委托(delegate)。当第一个线程尝试引发 SomeEvent 时,将抛出异常。避免这种情况的合理方法是:

protected virtual void OnSomeEvent(EventArgs args) 
{
    EventHandler ev = SomeEvent;
    if (ev != null) ev(this, args);
}

这是有效的,因为每当使用添加和删除访问器的默认实现将委托(delegate)添加到事件或从事件中删除时,就会使用 Delegate.Combine 和 Delegate.Remove 静态方法。这些方法中的每一个都返回一个委托(delegate)的新实例,而不是修改传递给它的委托(delegate)实例。

此外,.NET 中对象引用的赋值是 atomic , 添加和删除事件访问器的默认实现是 synchronised .所以上面的代码通过首先将多播委托(delegate)从事件复制到一个临时变量而成功。在此之后对 SomeEvent 的任何更改都不会影响您制作和存储的副本。因此,您现在可以安全地测试是否有任何委托(delegate)已注册并随后调用它们。

请注意,此解决方案解决了一个竞争问题,即事件处理程序在被调用时为 null 的问题。它不处理事件处理程序在调用时失效的问题,或者事件处理程序在获取副本后订阅的问题。

例如,如果事件处理程序所依赖的状态在处理程序取消订阅后立即销毁,那么此解决方案可能会调用无法正常运行的代码。参见 Eric Lippert's excellent blog entry更多细节。另请参阅 this StackOverflow question and answers .

编辑:如果您使用的是 C# 6.0,则 Krzysztof's answer看起来是个不错的选择。

关于c# - 在事件分派(dispatch)之前检查 null ...线程安全吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/282653/

相关文章:

c# - 在 XAML 中将动画绑定(bind)到 ListViewItem

c# - 检查控制计时器的线程的状态

Java 并发实践 “Listing 7.15. Adding reliable cancellation to LogWriter.”。 loggerThread.interrupt() 有什么意义?

c# - 如何在c#中正确关闭线程

c# - 使用 .NET 的报告和数据可视化?

c# - 让1个线程同时执行多个方法

javascript - 在纯 Javascript 中每次单击都会更改背景颜色

javascript - 获取触发元素

javascript - 事件发生后阻止特定功能

c# - async/await Tcp socket 实现只接受一个连接