多篇文章说,在 .NET 中实现双重检查锁定时,您要锁定的字段应该应用 volatile 修饰符。但究竟是为什么?考虑以下示例:
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
}
为什么“lock (syncRoot)”不能实现必要的内存一致性?在“锁定”语句之后,读取和写入都将是易变的,因此必要的一致性将得以实现,这不是真的吗?
最佳答案
volatile 是不必要的。好吧,有点**
volatile 用于在变量的读写之间创建内存屏障*。
lock 在使用时会导致在 lock 内的 block 周围创建内存屏障,此外还会将对该 block 的访问限制为一个线程。
内存屏障使得每个线程读取变量的最新值(不是缓存在某个寄存器中的本地值)并且编译器不会重新排序语句。使用 volatile 是不必要的**,因为您已经获得了锁。
Joseph Albahari比以往任何时候都更好地解释这些东西。
一定要查看 Jon Skeet 的 guide to implementing the singleton在 C# 中
更新:
*volatile 导致变量的读取为 VolatileRead 并写入为 VolatileWrite,在 CLR 上的 x86 和 x64 上,实现为一个MemoryBarrier。它们在其他系统上的粒度可能更细。
**只有在 x86 和 x64 处理器上使用 CLR 时,我的回答才是正确的。它可能在其他内存模型中是正确的,例如在 Mono(和其他实现)、Itanium64 和 future 的硬件上。这就是 Jon 在他的双重检查锁定的“陷阱”一文中所指的内容。
执行以下操作之一{将变量标记为 volatile,使用 Thread.VolatileRead 读取它,或插入对 Thread.MemoryBarrier 的调用}可能是代码在弱内存模型情况下正常工作所必需的。
据我了解,在 CLR 上(甚至在 IA64 上),写入永远不会重新排序(写入始终具有释放语义)。但是,在 IA64 上,读取可能会被重新排序以在写入之前进行,除非它们被标记为 volatile 。不幸的是,我无法访问 IA64 硬件来玩,所以我说的任何事情都是猜测。
我还发现这些文章很有帮助:
http://www.codeproject.com/KB/tips/MemoryBarrier.aspx
vance morrison's article (一切都链接到这里,它谈论双重检查锁定)
chris brumme's article (一切都链接到这个)
Joe Duffy: Broken Variants of Double Checked Locking
luis abreu 的多线程系列也很好地概述了这些概念
http://msmvps.com/blogs/luisabreu/archive/2009/06/29/multithreading-load-and-store-reordering.aspx
http://msmvps.com/blogs/luisabreu/archive/2009/07/03/multithreading-introducing-memory-fences.aspx
关于c# - .NET 双重检查锁定中对 volatile 修饰符的需求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1964731/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)
我有一个包含多个键的散列和一个字符串,该字符串不包含散列中的任何键或包含一个键。h={"k1"=>"v1","k2"=>"v2","k3"=>"v3"}s="thisisanexamplestringthatmightoccurwithakeysomewhereinthestringk1(withspecialcharacterslike(^&*$#@!^&&*))"检查s是否包含h中的任何键的最佳方法是什么,如果包含,则返回它包含的键的值?例如,对于上面的h和s的例子,输出应该是v1。编辑:只有字符串是用户定义的。哈希将始终相同。 最佳答案
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
我的日期格式如下:"%d-%m-%Y"(例如,今天的日期为07-09-2015),我想看看是不是在过去的七天内。谁能推荐一种方法? 最佳答案 你可以这样做:require"date"Date.today-7 关于ruby-检查日期是否在过去7天内,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/32438063/
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha