线程安全的事件处理程序调用。避免NullReference

时间:2017-10-25 20:36:46

标签: .net

有很多讨论 if (SomeEvent != null) SomeEvent(this, null); 可能产生NullReference。

SomeEventHandler temp = SomeEvent; if (temp != null) temp(this, null); 稍微好一点,但编译器可以优化temp变量,我们会回到问题。

然后解决这个问题应该是稳定的 SomeEventHandler temp = Interlocked.CompareExchange(ref SomeEvent, null, null) if (temp!=null) { temp(this, e) }

我可能会误解事件链的维护方式。 这是否意味着当链内的事件被取消订阅时,它的占位符变为空?!

让我们说SomeEvent = {eventHandler1,eventHandler2,eventHandler3}。 某些线程取消订阅SomeEvent中的eventHandler2。 不应该从第一个exerpt中SomeEvent(this, e)只调用eventHandler1而不是eventHandler3吗?

为什么选择NullReference?

我可以理解,当没有订户存在时,SomeEvent为null。 但是,解决方案是值得怀疑的,因为如果订阅者取消订阅,那么我们就不应该强行解雇它。

我可能错过了一些关于事件的事情。请解释一下。

如果null只能出现没有剩余处理程序的结果, 比整个解决方案都值得怀疑,如果不是愚蠢的话。 因为我为什么要在取消订阅的对象上调用事件处理程序(现在可能正在处理并且处于不稳定状态)!

1 个答案:

答案 0 :(得分:0)

  

为什么选择NullReference?

事件的后备存储是委托对象。代表是不可改变的。取消订阅事件会调用MulticastDelegate.RemoveImpl()来创建新对象。看看代码,注意当没有调用时它返回null。所以是的,SomeEvent可以在您忙于提升事件时变为null。经典的线程竞赛错误。保留副本可防止客户端代码使其为空。

  

如果不是愚蠢的话,整个解决方案是值得怀疑的

是。如果您需要此代码来执行预期的操作,则会出现问题。有了解决方法,事件订阅者将获得该事件,即使它在几纳秒之前取消订阅。另一个线程竞赛错误。

然而,重要的是谁是愚蠢的。取消订阅该事件的代码通常由其他人编写。始终是框架中事件的情况,在您自己的代码中并不常见。如果该客户端代码使用线程并且它没有正确锁定,那么您的代码会崩溃。不是愚蠢的代码。

您不希望该错误报告。

客户端代码可以处理竞争。它造成了它,它可以做些什么。你不能。