触发事件时避免竞争条件(在多线程应用程序中)的常见做法是:
EventHandler<EventArgs> temp = SomeEvent;
if (temp != null) temp(e);
"Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible."
问题(根据本书)是“编译器可以优化此代码以完全删除局部临时变量。如果发生这种情况,此版本的代码与第一个版本相同,因此仍然可能出现 NullReferenceException”
根据 CLR 通过 C#,这是强制编译器复制事件指针的更好方法。
virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp =
Interlocked.CompareExchange(ref NewMail, null, null);
if (temp != null)
temp(this, e);
}
在这里,如果 NewMail 引用为 null,CompareExchange 将其更改为 null,如果它不为 null,则不会更改 NewMail。换句话说,CompareExchange 根本不会更改 NewMail 中的值,但它会以原子、线程安全的方式返回 NewMail 中的值。杰弗里里希特 (2010-02-12)。通过 C# 进行 CLR(第 265 页)。OReilly Media - A. Kindle 版。
我在 .Net 4.0 框架上,不确定这可能如何工作,因为 Interlocked.CompareExchange 需要对位置的引用,而不是对事件的引用。
要么这本书有错误,要么我误解了它。有没有人实现过这个方法?或者有更好的方法来防止这里的竞争条件?
更新
这是我的错误,锁定代码有效。我只是指定了错误的铸造,但根据布拉德利(下),在 .net 2.0 及更高版本的 Windows 中没有必要。