Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 什么样的';挥发性';在.NET中的双重检查锁定中需要操作_C#_.net_Multithreading - Fatal编程技术网

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

C# 什么样的';挥发性';在.NET中的双重检查锁定中需要操作,c#,.net,multithreading,C#,.net,Multithreading,我从Joe Duffy的书《windows上的并发编程》中获取了DCL的代码 class LazyInit,其中T:class { 私有挥发性T m_值; 私有对象m_sync=新对象(); 私营Func m_工厂; 公共LazyInit(Func工厂){m_工厂=工厂;} 公共价值 { 得到 { 如果(m_值==null) { 锁定(m_同步) { 如果(m_值==null) { m_值=m_工厂(); } } } 返回m_值; } } } 据说,将m_值标记为volatile可以防止写操作

我从Joe Duffy的书《windows上的并发编程》中获取了DCL的代码

class LazyInit,其中T:class
{
私有挥发性T m_值;
私有对象m_sync=新对象();
私营Func m_工厂;
公共LazyInit(Func工厂){m_工厂=工厂;}
公共价值
{
得到
{
如果(m_值==null)
{
锁定(m_同步)
{
如果(m_值==null)
{
m_值=m_工厂();
}
}
}
返回m_值;
}
}
}
据说,将m_值标记为volatile可以防止写操作重新排序,这将导致其他线程获得“未初始化字段的非空对象”。如果问题只是因为可能的写入重新排序而发生,我可以使用“Volatile Write”而不是像下面那样标记字段Volatile吗?(这段代码看起来有点难以演示,我只是想确定我们是否只能使用volatile write)

class LazyInit,其中T:class
{
私有对象m_值;
私有对象m_sync=新对象();
私营Func m_工厂;
公共LazyInit(Func工厂){m_工厂=工厂;}
公共价值
{
得到
{
如果(m_值==null)
{
锁定(m_同步)
{
如果(m_值==null)
{
VolatileWrite(参考m_值,m_工厂());
}
}
}
返回(T)m_值;
}
}
}
一个相关的问题是该书的互锁版本

class LazylnitRelaxedRef其中T:class
{
私有挥发性T m_值;
私营Func m_工厂;
公共LazylnitRelaxedRef(Func工厂){m_工厂=工厂;}
公共价值
{
得到
{
如果(m_值==null)
Interlocated.CompareExchange(参考m_值,m_工厂(),null);
返回m_值;
}
}
}

由于ECMA-CLI规定了“联锁操作执行隐式获取/释放操作”,在这种情况下,我们还需要volatile吗?

首先,处理volatile非常困难,所以不要太随便!但是,这是一个非常接近你的问题的答案,我认为每个人在使用关键字
volatile
之前,以及在开始使用
volatireRead
volatireWrite
MemoryBarrier
之前,都应该阅读这篇文章

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

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

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

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

ECMA内存模型和.NET2内存模型之间有一个重要的区别。为什么需要执行双重检查锁定?您是否发现您的应用程序在获取锁时存在性能问题?@CodeInChaos,请详细说明“重要区别”是什么。@CodeInChaos,@AllonGuralnek,我知道.NET 2有一个强大的MM。因为很多材料在构造singleton对象时都谈到写重排序问题,因为volatile write可以防止这个问题,我想知道,如果只使用volatile write而不使用volatile read,在较弱的内存模型(如ECMA)中是否会有一些问题?对不起,我应该问一下,代码误导了您,对于DCL,如果读取不是易失性的,或者没有在读取时应用acquire fence,那么在一些较弱的内存模型中是否存在任何问题?顺便说一句,.net对API Thread.volatireRead和Thread.volatireWrite的实现使用了完全隔离,这意味着它们比volatile read和volatile write更强。对于我的问题,让我们考虑一下易失性读情况,而不是API一。(再次,由于代码误导了您),原始的易失性行为是在写之前和读之后设置一个屏障。我建议在写操作前后和读操作之前设置一个内存屏障。如果我们在写操作之后立即读操作,那么这两个操作之间可能会因为没有障碍而被交换。@Ivaylo Slavov,对于“交换”,您能给我更多关于在这种情况下会发生什么情况来破坏DCL的信息吗?顺便说一句,这种“交换”会打破数据依赖关系吗?@Fei,“交换”是指处理器执行读写操作的顺序。如果您对标准易失性字段进行了读操作并在读操作之后执行了写操作,那么很可能在读操作之前执行写操作,因为它们之间没有障碍。当使用Thread.MemoryBarrier时,它确保屏障之前的操作永远不会与屏障之后的操作交换。交换通常取决于处理器体系结构和代码运行的系统,但深入研究这一问题会使主题复杂化