多线程下的无锁编程与memory barrier
今天看twitter的EarlyBird论文,提到了用volatile关键字,还有single-writer+multi-reader实现了无锁的更新查询机制,其中提到了JVM的内存模型和Memory Barrier概念。想到去年做Ksearch的时候,也用c++的volatile关键字做了无锁的更新查询,但c++的volatile语义很明显比java的volatile弱,不包含memory barrier概念,理论上说我当时的做法是有问题的。最近花了不少时间,把相关的概念整理了一下,有些理解可能还不是很透彻,需要以后实践中加深学习。
为了搞清楚无锁编程(lock-free),涉及到的主要概念:编译器优化(比如代码调整,register优化),volatile关键字,memory-barrier(mfence),CPU cache。
先说编译器优化,比如register优化,如果register有空闲,一些函数内部的局部变量,不用在内存中分配,而直接使用register;代码调整,比如for-loop的展开等;这些都是在编译阶段做的。volatile关键字的语义是告诉编译器,这个变量不能优化到register,每次读写直接走内存;volatile经常会用在device驱动中,因为线程之间不知道什么时候会修改,所以编译器不能做register优化。还有一个volatile的用法例子,经常用到,如下所示。
这个例子是一个多更新多查询的例子,而且更新查询之间无锁,很能说明问题,摘自:Memory Barriers: a Hardware View for Software Hackers。
总结:对我自己来说,原来做多线程编程,如果涉及到共享数据,我一般使用mutex等原语来实现;而且以为mutex只提供了共享数据的互斥访问,对于其额外提供的memory barrier完全不知道。当去年做ksearch时,我就实现了一个lock-free的查询更新结构,以为是正确的,其实完全没考虑memory-barrier,有一定出错几率,现在每天支撑几亿的请求量,真是奇迹啊。
顺便说下,java中的volatile关键字包含了memory barrier概念,与c/c++中的不一样,所以earlybird可以这么用。
参考资料:
http://www.domaigne.com/blog/computing/mutex-and-memory-visibility/
http://www.oaklib.org/docs/oak/singleton.html
http://www.parallellabs.com/2010/12/04/why-should-we-be-care-of-volatile-keyword-in-multithreaded-applications/
http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/
Memory Barriers: a Hardware View for Software Hackers
In what situations might I need to insert memory barrier instructions
How do I Understand Read Memory Barriers and Volatile