触发事件时避免竞争条件(在多线程应用程序中)的常见做法是:
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 via 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;如果 NewMail 不为 null,则不会更改 NewMail。换句话说,CompareExchange 根本不会更改 NewMail 中的值,但它确实以原子、线程安全的方式返回 NewMail 中的值。
杰弗里·里克特 (2010-02-12)。通过 C# 的 CLR(第 265 页)。 OReilly Media - A. Kindle 版。
我使用 .Net 4.0 框架,不确定这如何工作,因为 Interlocked.CompareExchange 需要对位置的引用,而不是对事件的引用。
要么是书上有错误,要么是我理解错了。有人实施过这个方法吗?或者有更好的方法来防止这里的竞争条件?
UPDATE
这是我的错误,iterlocked 代码有效。我只是指定了错误的转换,但根据 Bradley(如下)的说法,在 Windows 上的 .net 2.0 及更高版本中没有必要。