您的位置:首页 > 其它

多线程开发的一些基础知识

2016-11-21 23:19 295 查看
CPU读写的原子性:

CPU对一个byte/word/32bit/64bit(需对齐,对齐一般由编译器实现)的读写保证原子性,例如不会发生刚读写完低16bit、读高16bit时被阻塞的情况。

这样“ullx = 10”是原子的,但“ullx += 1”(会被编译成多条汇编)不是原子的;ullx=ully同理。此时需使用原子变量和操作。

对于位域,

struct A{

int a:1;

int b:15;

};

由于CPU实际是同时将a/b读入或修改,因此它们需要用同一把锁保护。

volatile

变量可能被以一种编译器未知的方式修改,因而要求编译器每次都从特定地址读取变量的值,防止编译器优化引入问题,例如:

int i=0;

int a=i;

//用汇编修改i

b=i

优化后b可能不再从内存中读取i,而是直接使用寄存器中的值,导致汇编直接修改的内存中的i可能不被读取到。

volatile

作用:要求编译器每次都从内存中读取值,不做优化;

例子:

1,中断处理中访问非auto变量;

2,线程中被几个任务共享的变量——线程是运行期概念,编译器无法确定线程运行全景;

3,共享内存中,可能被其他进程改变的值;

4,内存可能被物理设备修改;

const与volatile,可以同时使用:

const只是告诉编译器变量不会被修改,但不表明它运行期间不被意外修改,也无法避免其被绕过而修改。

标准C/C++中,volatile不保证原子性,也不能保证顺序性。

double-checked的失效

原因:h = new H(),会首先分配一块内存并赋值给h,然后再内存上执行构函,这样别的线程可能得到一个未初始化的对象;java如此,C++不确定,依赖具体编译器、运行环境;

方法:java,volatile能够保证先构造对象再赋值;C++,可使用显式的memory barrier

参考:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html https://segmentfault.com/a/1190000000382965
CC,cache一致性(硬件保证的机制):硬件通过一致性协议,保证一个CPU的cache变化,另一个CPU能马上读到最新的值;

多核环境,CPU首先将内存读入L1/L2 cache,变量被修改时,可能别的CPU的cache已经老化,此时通过CC保证一致性。

False Sharing:

为多线程准备一个数组,每个线程使用其中一项,来防止冲突避免加锁,可能达不到预期的性能优化效果,原因是多核环境,为保证多核的cache一致性,CPU会读入整个cache line(CPU属性的cache_line_align)到cache,修改其中一项,会导致其他项也要在不同core和cache间同步,从而影响性能。

方案:每项按cache line凑齐;

SC,顺序一致性:

线程1:

a=10

flag=true

线程2:

wait(flag=true)

  print a;

如果不保证SC,print a的结果可能不为10

乱序优化:x86仅允许将不同地址的load操作优化到store操作之前。

Data Race:多个线程中对同一个数据项有读写冲突;
SC for data-race free programs:程序员保证程序没有data-race,CPU、编译器保证优化后的运行结果等同SC;

acquire语义:当前线程中,必须先完成本变量的读,再执行所有全局变量的访问---读优先;
release语义:当前线程中,必须先执行所有全局变量的访问,再执行对本变量的写 ---写靠后;

学习笔记,主要参考 http://www.parallellabs.com
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: