在下面的文章中,Stephen Cleary向我们提供了有关如何制作线程安全事件的建议:
http://www.codeproject.com/Articles/37474/Threadsafe-Events.aspx
作者在解决方案#2中解释了为什么以下代码出错:
MyEventHandler myEvent = this.MyEvent;
if (myEvent != null)
{
myEvent(this, args);
}
活动代表的副本可能已过时。斯蒂芬为这个问题提供了以下解决方案。
没有陷入痛苦的细节,为了确保这一点 一个人必须读取非易失性字段的当前值 发出内存屏障或将复制操作包装在锁中 (并且它必须与事件添加/删除获取的锁相同 方法)。
我对细节很感兴趣,但遗憾的是他并没有解释。在这里使用内存屏障时技术上会发生什么?与上述方法有什么不同?
答案 0 :(得分:1)
您发布的代码的问题是编译器/ jitter / cpu可能会重新排序this.MyEvent
字段的读取,并且会及时移回。换句话说,您可能会看到之前读过的缓存值。
为了防止这种重新排序,你发出一个内存屏障(又名完整的内存栅栏),告诉所有3个参与者不要在栅栏上方(或下方)移动任何指令。这将阻止读取被缓存,或者被“合并”#34;与其他早期的读物。
Thread.MemoryBarrier();
//--> no instructions can be moved above or below the fence <--
MyEventHandler myEvent = this.MyEvent;
if (myEvent != null)
{
myEvent(this, args);
}
另外,请注意该博客帖子上的日期,它已经很老了。 从那时起,C#4.0的发布就重新编写了事件。
Stephen Cleary的前提是使用lock (this)
实现事件已不再适用。事件现在是无锁的。你可以在这里看到Chris Burrows详细介绍的内容:Events get a little overhaul in C# 4, Part I: Locks。
您可能也有兴趣阅读本系列的其他3个部分: