多核程序数据同步
概念:
多核:是指多个物理核心,这些核心可能在一个物理处理器上,也可能分布在多个物理处理器。
多核程序需要注意共享数据的同步问题,主要包含
1、原子性:读写数据的多次操作不能被中断。比如a=1,这句话会被编译为多条汇编指令,这几条指令执行过程中如果被打断,可能导致其他程序读出脏数据,严重的导致程序崩溃。
2、可见性:对于多线程共享的数据,其中一个线程修改了数据,其他线程有可能读到的还是原值,甚至一直读不到修改后的值。
3、顺序性:程序逻辑的顺序执行。
下面具体探讨这三种现象出现的原因及应对方案:
1、原子性:
编写程序的一个语句会被编译为多条编译指令,被cpu依次执行,共享的数据可能在这些操作未完成之前被其他线程读写到,同时操作系统也会产生中断,导致指令的执行不是原子的。
2、可见性:
多核系统中,一般每个核都有自身的高速缓存(L1、L2级高速缓存)以及寄存器,这些存储单元会缓存数据以提高性能,这就造成了一个线程修改了共享数据后,其他的线程并不一定看到。为了解决该问题,硬件层都存在cache一致性协议MESI,保证了共享数据的可见性,但这会严重影响处理器性能,因此实际处理器架构中都增加了store buffer之类的结构,这个结构不是cache,因此单单依靠MESI在实际编程时已无法保证可见性。
3、顺序性
编译器为了提高程序性能会对程序进行优化,即指令重排,打乱编程时书写的顺序,这往往出现在c程序使用-O2级别时。指令重排单线程情况下不会出现问题,但对于严格依赖共享数据读写顺序的多线程程序,则会出现不可预见的效果。
其次,程序执行时,处理器为了提高利用率,指令可能被乱序执行。以上两个方面造成多核程序可能不会按照预先设计的顺序执行程序语句,导致程序的执行效果和预期不一致,甚至崩溃。
解决方案
1、volatile能保证可见性,顺序性,不能保证原子性
2、加锁能保证原子性、可见性、顺序性
3、c++11的atomic配合std::memory_order_seq_cst也能保证原子性、可见性、顺序性