事件调度前检查null ...线程安全? [英] Checking for null before event dispatching... thread safe?

查看:94
本文介绍了事件调度前检查null ...线程安全?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

某些令我困惑的东西,但从未造成任何问题...推荐的发送方式如下:

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

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

解决方案正如你所指出的,多线程可以同时访问 SomeEvent ,一个线程可以检查 SomeEvent 为null并确定它不是。在这样做之后,另一个线程可以从 SomeEvent 中删除​​最后注册的代理。当第一个线程尝试提升 SomeEvent 时,将抛出异常。避免这种情况的合理方法是:

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

这是有效的,因为每当使用添加和删​​除访问器的默认实现,使用Delegate.Combine和Delegate.Remove静态方法。这些方法中的每一个返回一个委托的新实例,而不是修改传递给它的一个。



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



请注意,此解决方案解决了一个种族问题,即事件处理程序为null时调用。它不处理事件处理程序在被调用时失效的问题,或事件处理程序在执行复制后进行预订。



例如,如果事件处理程序依赖于处理程序未订阅时立即被破坏的状态,则此解决方案可能会调用无法正常运行的代码。请参阅 Eric Lippert的优秀博客条目更多细节。另请参见此StackOverflow问题和答案


Something that confuses me, but has never caused any problems... the recommended way to dispatch an event is as follows:

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

In a multi-threaded environment, how does this code guarantee that another thread will not alter the invocation list of SomeEvent between the check for null and the invocation of the event?

解决方案

As you point out, where multiple threads can access SomeEvent simultaneously, one thread could check whether SomeEventis null and determine that it isn't. Just after doing so, another thread could remove the last registered delegate from SomeEvent. When the first thread attempts to raise SomeEvent, an exception will be thrown. A reasonable way to avoid this scenario is:

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

This works because whenever a delegate is added to or removed from an event using the default implementations of the add and remove accessors, the Delegate.Combine and Delegate.Remove static methods are used. Each of these methods returns a new instance of a delegate, rather than modifying the one passed to it.

In addition, assignment of an object reference in .NET is atomic, and the default implementations of the add and remove event accessors are synchronised. So the code above succeeds by first copying the multicast delegate from the event to a temporary variable. Any changes to SomeEvent after this point will not affect the copy you've made and stored. Thus you can now safely test whether any delegates were registered and subsequently invoke them.

Note that this solution solves one race problem, namely that of an event handler being null when it's invoked. It doesn't handle the problem where an event handler is defunct when it's invoked, or an event handler subscribes after the copy is taken.

For example, if an event handler depends on state that's destroyed as soon as the handler is un-subscribed, then this solution might invoke code that cannot run properly. See Eric Lippert's excellent blog entry for more details. Also, see this StackOverflow question and answers.

这篇关于事件调度前检查null ...线程安全?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆