当前位置:  开发笔记 > 编程语言 > 正文

在事件调度之前检查null ...线程安全吗?

如何解决《在事件调度之前检查null线程安全吗?》经验,为你挑选了3个好方法。

让我感到困惑的东西,但从来没有引起任何问题......推荐的事件发送方式如下:

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

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



1> RoadWarrior..:

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

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

这是有效的,因为只要使用add和remove访问器的默认实现向事件添加或删除委托,就会使用Delegate.Combine和Delegate.Remove静态方法.这些方法中的每一个都返回一个新的委托实例,而不是修改传递给它的那个实例.

此外,在.NET中分配对象引用是原子的,并且同步添加和删​​除事件访问器的默认实现.因此,上面的代码首先将多播委托从事件复制到临时变量.在此之后对SomeEvent的任何更改都不会影响您制作和存储的副本.因此,您现在可以安全地测试是否已注册任何代理并随后调用它们.

请注意,此解决方案解决了一个争用问题,即在调用时事件处理程序为null.它不处理事件处理程序在调用时失效的问题,或者事件处理程序在复制后订阅的问题.

例如,如果事件处理程序依赖于处理程序未订阅时销毁的状态,则此解决方案可能会调用无法正常运行的代码.有关详细信息,请参阅Eric Lippert的优秀博客文章.另外,请参阅此StackOverflow问题和答案.

编辑:如果您使用C#6.0,那么Krzysztof的答案看起来是一个很好的方法.



2> Krzysztof Br..:

在C#6.0中,您可以使用monadic Null-conditional运算符?.来检查null并以简单且线程安全的方式引发事件.

SomeEvent?.Invoke(this, args);

它是线程安全的,因为它只评估左侧一次,并将其保存在临时变量中.您可以在这里阅读更多部分标题为Null-conditional运算符.



3> Cherian..:

删除此null检查的最简单方法是将eventhandler分配给匿名委托.罚款很少,并使您免除所有空检,竞争条件等.

public event EventHandler SomeEvent = delegate {};

相关问题:在事件声明中添加匿名空委托是否有缺点?


我不同意.该方法看起来像一个黑客,并没有使触发事件"安全"或可靠,它只解决了空检查问题.请在相关主题中查看我的解释.
推荐阅读
kikokikolove
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有