问题描述
东西让我困惑,但从未引起任何问题......在分发事件推荐的方法如下:
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();
}
在多线程环境下,如何做到这一点code保证其他线程不会改变检查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?
推荐答案
正如你指出的,在多个线程可以访问 SomeEvent
同时,一个线程可以检查是否 SomeEvent
为空,并确定它是不是。只是这样做之后,另一个线程可能会删除最后一个登记的代表从 SomeEvent
。当第一个线程试图筹集 SomeEvent
,一个异常将被抛出。一个合理的方式来避免这种情况是:
As you point out, where multiple threads can access SomeEvent
simultaneously, one thread could check whether SomeEvent
is 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);
}
这工作,因为当一个委托使用添加的默认实现和remove访问添加或删除从一个事件中,使用Delegate.Combine和Delegate.Remove静态方法。每个方法都返回一个代表的新实例,而不是修改一个传递给它。
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.
此外,在.NET对象引用的分配是atomic,并且加载的默认实现,并删除事件访问是。因此,code以上,首先抄袭事件的多播委托给一个临时变量成功。这一点后SomeEvent的任何变化都不会影响你做并保存副本。因此,你现在可以安全地测试任何代表是否注册,随后调用它们。
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.
例如,如果一个事件处理程序取决于公司尽快处理程序是未认购破坏状态,那么这个解决方案可能会调用code不能正常运行。请参见更多细节。此外,请参阅的。
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.
这篇关于检查事件分派前空...线程安全的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!