在多线程同步过程中,一般涉及到变量(其实是对应的操作)的原子性、可见性与顺序性三种性质。C++11提供的原子变量,是满足其中的原子性的(从名字也可以看出来)。至于其可见性和顺序性,网上很多资料说的不够准确,容易让人误解。
先说可见性,这里说的可见性并不是原子变量本身的可见性,不是你在线程A里修改了原子变量a,然后在线程B里能发现a的值变了。当然这事情是对的,但这个事情跟原子变量没关系,所有全局变量都可以做到。
再说顺序性,这里说的顺序性不是同一个线程中的语句是按顺序执行的。比如
int a = 0;
int b = 0;
a = 1;
b = a;
std::cout << b << std::endl;
这个地方输出的b肯定是1,也就是a = 1一定在b=a前执行,这件事情跟原子变量没关系,你用普通的变量也是一样的。但如果你把b=a,改成b=2,那是先执行a=1还是先执行b=2就不一定了。也就是说,在同一个线程中,如果句子之间有逻辑关系,那一定是按这个逻辑关系执行的,这个顺序性是语言的基本性质,跟原子变量无关。
那原子变量的顺序性如何体现呢,一定是在跨线程之间。就像上面说到的如果是b=2,那是先执行a=1还是b=2是不确定的,是可能会重排的,这种重排对本线程是没任何影响的,因为两句话没有依赖关系,先执行谁都一样。但这个时候,假如a和b都是全局变量,在另一个线程里也被访问了,那先执行a=1还是先执行b=2就不一样了。比如另一个线程是这样的:
if (b == 2) {
cout << a << endl;
}
这里是输出1还是0呢?
所以这里说的顺序性,是由于一个线程内的语句重排了(这种重排对本线程结果无影响),导致其他线程的结果不确定了,这就是所说的没保证顺序性。
那如何保证顺序性呢,需要用到原子变量的内存序概念,也就是std::memory_order_acquire,std::memory_order_release等几个参数,这个就不详细展开了,因为相关文章已经很多了,这里主要是强调理解的时候,一定要把上面这句话搞懂,是本线程内重排导致了其他线程的问题,所以要采用这种方式。