什么样的'挥发性'在.net的双重检查锁定中需要操作

本文关键字:锁定 检查 操作 net 挥发性 什么样 | 更新日期: 2023-09-27 18:15:53

我从Joe Duffy的书《Concurrent programming on windows》中获取了DCL的代码

class LazyInit<T> where T : class
{
private volatile T m_value;
private object m_sync = new object();
private Func<T> m_factory;
public  LazyInit(Func<T>  factory)  {  m_factory  =  factory;  }
public T value
{
  get
  {
    if (m_value == null)
    {
      lock (m_sync)
      {
        if (m_value == null)
        {
          m_value = m_factory();
        }
      }
    }
    return m_value;
  }
}
}

据说标记m_value volatile可以防止写重排序,这将导致其他线程获得"具有未初始化字段的非空对象"。如果问题只是因为可能的写重新排序而发生,我可以使用"易失性写"而不是标记该字段易失性,如下所示?(这段代码看起来有点别扭,我只是想确定我们是否只能使用volatile write来代替)

class LazyInit<T> where T : class
{
private object m_value;
private object m_sync = new object();
private Func<T> m_factory;
public  LazyInit(Func<T>  factory)  {  m_factory  =  factory;  }
public T value
{
  get
  {
    if (m_value == null)
    {
      lock (m_sync)
      {
        if (m_value == null)
        {
          Thread.VolatileWrite(ref m_value, m_factory());
        }
      }
    }
    return (T)m_value;
  }
}
}

一个相关的问题是来自书

的联锁版本
class LazylnitRelaxedRef<T> where T : class
{
private volatile T m_value;
private Func<T> m_factory;
public LazylnitRelaxedRef(Func<T> factory) { m_factory = factory; }
public T Value
{
  get
  {
    if (m_value == null)
      Interlocked.CompareExchange(ref  m_value, m_factory(), null);
    return m_value;
  }
}
}

既然ECMA-CLI规范了"联锁操作执行隐式获取/释放操作",在这种情况下我们还需要volatile吗?

什么样的'挥发性'在.net的双重检查锁定中需要操作

首先,打乱volatile 真的很难,所以不要太随意了!但是,这里有一个非常接近你的问题的答案,这里有一篇文章,我认为每个人在使用关键词volatile之前都应该阅读,当然在开始使用VolatileRead, VolatileWriteMemoryBarrier之前。

第一个链接的答案是:不,你不需要使用volatile,你只需要在分配新值之前使用System.Threading.Thread.MemoryBarrier()。这是因为使用volatile关键字时隐含的release_fence确保它完成向主存的写入,并且在它完成之前不能执行任何读/写操作。

那么,Thread.VolatileWrite()做什么,它是否执行我们从'volatile'关键字获得的相同功能?下面是这个函数的完整代码:

public static void VolatileWrite (ref int address, int value)
{
  MemoryBarrier(); address = value;
}

是的,它在赋值之前调用MemoryBarrier,这就足够了!