使用内存屏障的线程安全事件
本文关键字:线程 安全事件 内存 | 更新日期: 2023-09-27 18:13:10
在接下来的文章中,Stephen Cleary给出了如何创建线程安全事件的建议:
http://www.codeproject.com/Articles/37474/Threadsafe-Events.aspx作者在解决方案#2中解释了为什么以下代码是错误的:
MyEventHandler myEvent = this.MyEvent;
if (myEvent != null)
{
myEvent(this, args);
}
事件委托的副本可能过期。Stephen为这个问题提供了如下的解决方案。
我对他没有解释的细节很感兴趣。在这里使用内存屏障在技术上发生了什么?与上面的方法有什么不同?在不深入细节的情况下,为了确保一个是读取非易失性字段的当前值,一个是必须的发出内存屏障或将复制操作包装在锁中(并且必须是add/remove事件获取的同一个锁方法).
您发布的代码的问题是this.MyEvent
字段的读取可能由编译器/抖动/cpu重新排序,并及时移动回来。换句话说,您可能会看到不久前读取的缓存值。
为了防止这种重新排序,你发出一个内存屏障(又名全内存屏障),它告诉所有3个参与者不要移动任何指令在栅栏之上(或之下)。这将防止读取被缓存,或与其他较早的读取"合并"。
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的详细内容:事件在c# 4中得到了一些改进,第一部分:锁。
您可能也有兴趣阅读本系列的其他三部分:
- 事件在c# 4中得到了一些改进,第2部分:语义变化和+=/-=
- 事件在c# 4中得到了一些改进,第三部分:突破性的变化
- 事件在c# 4中得到了一些修改,之后:有效事件