什么样的'挥发性'在.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吗?
首先,打乱volatile 真的很难,所以不要太随意了!但是,这里有一个非常接近你的问题的答案,这里有一篇文章,我认为每个人在使用关键词volatile
之前都应该阅读,当然在开始使用VolatileRead
, VolatileWrite
和MemoryBarrier
之前。
第一个链接的答案是:不,你不需要使用volatile,你只需要在分配新值之前使用System.Threading.Thread.MemoryBarrier()
。这是因为使用volatile关键字时隐含的release_fence
确保它完成向主存的写入,并且在它完成之前不能执行任何读/写操作。
那么,Thread.VolatileWrite()做什么,它是否执行我们从'volatile'关键字获得的相同功能?下面是这个函数的完整代码:
public static void VolatileWrite (ref int address, int value)
{
MemoryBarrier(); address = value;
}
是的,它在赋值之前调用MemoryBarrier,这就足够了!