在事件派发之前检查null …线程安全吗?

一些让我困惑的东西,但从来没有造成任何问题…推荐的事件派发方式如下:

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

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

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

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

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

另外,.NET中的对象引用的分配是primefaces的 ,并且添加和移除事件访问器的默认实现是同步的 。 所以上面的代码首先将事件中的多播委托复制到一个临时variables。 此后对SomeEvent的任何更改都不会影响您制作和存储的副本。 因此,您现在可以安全地testing任何代表是否已注册,然后调用它们。

请注意,这个解决scheme解决了一个竞争问题,即事件处理程序在被调用时为空。 它不处理事件处理程序在被调用时不存在,或者事件处理程序在获取副本后订阅的问题。

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

删除这个空检查最简单的方法是将事件处理程序分配给一个匿名代理。 很less的罚款和解除你所有的空白支票,竞赛条件等。

public event EventHandler SomeEvent = delegate {};

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

在C#6.0中,您可以使用monadic空条件运算符?. 以简单和线程安全的方式检查null和raise事件。

 SomeEvent?.Invoke(this, args); 

它是线程安全的,因为它只评估一次左侧,并将其保存在一个临时variables中。 您可以在这里阅读更多的部分标题为空条件运算符。

推荐的方法有点不同,并使用一个临时的如下:

 EventHandler tmpEvent = SomeEvent; if (tmpEvent != null) { tmpEvent(); } 

更安全的方法:

public class Test { private EventHandler myEvent; private object eventLock = new object(); private void OnMyEvent() { EventHandler handler; lock(this.eventLock) { handler = this.myEvent; } if (handler != null) { handler(this, EventArgs.Empty); } } public event MyEvent { add { lock(this.eventLock) { this.myEvent += value; } } remove { lock(this.eventLock) { this.myEvent -= value; } } } }
public class Test { private EventHandler myEvent; private object eventLock = new object(); private void OnMyEvent() { EventHandler handler; lock(this.eventLock) { handler = this.myEvent; } if (handler != null) { handler(this, EventArgs.Empty); } } public event MyEvent { add { lock(this.eventLock) { this.myEvent += value; } } remove { lock(this.eventLock) { this.myEvent -= value; } } } } 

-法案

我想build议通过使用EventHandler的扩展函数来稍微改进RoadWarrior的答案:

 public static class Extensions { public static void Raise(this EventHandler e, object sender, EventArgs args = null) { var e1 = e; if (e1 != null) { if (args == null) args = new EventArgs(); e1(sender, args); } } } 

随着范围的扩大,事件可以简单地通过以下方式提出:

类SomeClass {公共事件EventHandler MyEvent;

 void SomeFunction() { // code ... //--------------------------- MyEvent.Raise(this); //--------------------------- } 

}

c# 事件