什么是三重检查锁定模式 < P>:“现代C++设计:Andrei Alexandrescu应用的通用程序设计和设计模式” 第六章实施单身
即使你放了volatile,也不能保证双重检查锁定模式的安全性和便携性。为什么会这样 如果有人能把任何好的链接,解释什么是放松记忆模型,什么是双重检查模式的确切问题。{或者有人能解释} 我以前认为volatile解决了这个问题,但直到我读了这本书之后才发现它是不正确的。 即使你放了volatile,也不能保证双重检查锁定模式的安全性和便携性。为什么会这样 我将尝试提供一些上下文 在C++中,有三个()可移植用例用于代码>易失性< /代码>,它们都与多线程无关。Alexandrescu确实花了很长时间来哄骗C++的类型系统来帮助多线程编程,但仅此而已。请参阅David Butenhof关于comp.programming.threads的文章 关于这个限定符的混淆源于这样一个事实:对于某些编译器(例如英特尔的)什么是三重检查锁定模式 < P>:“现代C++设计:Andrei Alexandrescu应用的通用程序设计和设计模式” 第六章实施单身,c++,multithreading,C++,Multithreading,即使你放了volatile,也不能保证双重检查锁定模式的安全性和便携性。为什么会这样 如果有人能把任何好的链接,解释什么是放松记忆模型,什么是双重检查模式的确切问题。{或者有人能解释} 我以前认为volatile解决了这个问题,但直到我读了这本书之后才发现它是不正确的。 即使你放了volatile,也不能保证双重检查锁定模式的安全性和便携性。为什么会这样 我将尝试提供一些上下文 在C++中,有三个()可移植用例用于代码>易失性< /代码>,它们都与多线程无关。Alexandrescu确实花了很长
volatile
发挥了内存界限语义的作用——这是安全多线程所需要的。但这不是便携式的,不应该依赖
此外,大多数专业级编译器无法完美地实现volatile
。看
另一种语言Java有着完全不同的特性,它们确实涉及到内存限制,这一事实可能会让人感到困惑。但是,请注意,即使是双重检查的锁定。大多数情况下,使用您始终检查的简单锁定对于单例来说也足够快。如果经常访问单例变量或字段,则始终可以将其缓存在变量或字段中
在证明简单的锁不够快之前,不要考虑“黑客”来避免锁定。编写无bug的可移植线程代码已经够难了,而且不会给自己带来更多问题 我不清楚什么是“三重检查锁定”,但自从近十年前编译器编写者和硬件工程师开始从程序员手中夺走控制权以来,我的lazy init有三种状态: enum eINITIALIZATION_STATE{FIRST_INITIALIZER=0,INITIALIZING,INITIALIZED} (想象一下,如果您发送的库软件与该领域重要的多线程算法完全不兼容!但我为SMP机器编写代码已经30多年了,这是多核“革命”(HA!)在过去的十年里,编译器编写者和硬件工程师们要么不知道他们在破坏什么,要么他们认为这无关紧要……但他们的评论太多了!) 所以
enum eINITIALIZATION_STATE {FIRST_INITIALIZER=0,INITIALIZING,INITIALIZED} ;
然后您有一个初始化控制变量:
static int init_state; //its 0 initialized 'cause static
那么成语是:
IF(init_state == INITIALIZED)
return your pointer or what ever;//the "normal" post initialization path
ENDIF
IF(init_state == FIRST_INITIALIZER)
IF(compare_and_swap(&init_state,
INITIALIZING,
FIRST_INITIALIZER)==FIRST_INITIALIZER)
//first initializer race resolved - I'm first.
do your initialization here;
//And now resolve the race induced by the compiler writers and the HW guys
COMPILER_FENCE();//needs macro for portability
HARDWARE_FENCE();//needs macro for portability
//on intel using cas here supplies the HARDWARE_FENCE();
compare_and_swap(&init_state,INITIALIZER,INITIALIZING);
//now you can return your pointer or what ever or just fall through
ENDIF
ENDIF
DOWHILE(*const_cast<volatile const int*>(&init_state)!=INITIALIZED)
relinquish or spin;
ENDDO
return your pointer or what ever;
IF(初始状态==已初始化)
返回你的指针或任何东西//“正常”初始化后路径
恩迪夫
IF(init_state==第一个_初始值设定项)
如果(比较_和_交换(&init_状态),
初始化,
第一个初始值设定项)==第一个初始值设定项)
//第一个初始化器竞赛解决了-我是第一个。
在这里进行初始化;
//现在解决由编译器编写者和硬件人员引起的竞争
编译器_FENCE()//对可移植性的需求
硬件_围栏()//对可移植性的需求
//在使用cas的intel上,此处提供硬件_FENCE();
比较_和_交换(&init_状态、初始值设定项、初始化);
//现在你可以返回你的指针或任何曾经或刚刚跌破的东西
恩迪夫
恩迪夫
DOWHILE(*const_cast(&init_state)!=已初始化)
放弃或旋转;
恩多
返回你的指针或任何东西;
我不确定这是不是真的,但由于这3种状态,我怀疑这可能等同于(或至少类似于)所谓的“三重检查锁定”.这是一篇关于这方面的好文章:Protip:问题是通过不使用单例来解决的。@DeadMG:这是一种实用的方法,但理论也应该被用于提高编程技能。volatile是否产生内存限制取决于编译器。平台仅确定线程安全是否需要内存围栏,但您关于英特尔平台上内存围栏中的
volatile
的说法不正确<代码>易失性在MSVC 2008及更高版本上有,其他编译器也有,但英特尔不保证。@Ben Voigt:我是说英特尔的编译器。完全正确。过早优化或过度使用单例模式。出于好奇,IF
、ENDIF
和其他Pascal化宏有什么原因吗?不是为了代码-只是用代码和psuedo语言之间的某种语言表达的习惯用法,以获取本质。