使用内存屏障的线程安全事件

本文关键字:线程 安全事件 内存 | 更新日期: 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中得到了一些修改,之后:有效事件