原子操作在C++中的使用
前言
所谓原子操作,就是“不可中断的一个或一系列操作”。
在单核心处理器系统中,能够在一条机器指令中完成的操作都可以认为是原子操作,因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。
在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。
CPU对原子操作的影响
intel处理器
《Intel
64 and IA-32 Architectures Software Developer`s Manual》Volume3 System
Programming Guide,8.1.1 Guaranteed Atomic Operations中讲解的原子操作如下:
Intel486处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
Reading or writing a byte
读写一个byte
Reading or writing a word aligned on a 16-bit boundary
读写16bit(2byte)内存对齐的字(word)
Reading or writing a doubleword aligned on a 32-bit boundary
读写32bit(4byte)内存对齐的双字(dword)
The Pentium processor (and newer processors since) guarantees that
the following additional memory operationswill always be carried out
atomically:
Pentium系列处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
Reading or writing a quadword aligned on a 64-bit boundary
读写64bit(8byte)内存对齐的四字(quadword)
16-bit accesses to uncached memory locations that fit within a 32-bit data bus
使用16bit访问的未缓存的内存,并且这些内存适应32位数据总线(翻译不好)
The P6 family processors (and
newer processors since) guarantee that the following additional memory
operationwill always be carried out atomically:
P6系列处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line
对单个cache line中缓存地址的未对齐的16/32/64位访问(非对齐的数据访问非常影响性能)
Accesses
to cacheable memory that are split across cache lines and page
boundaries are not guaranteed to beatomic by the Intel Core 2 Duo,
Intel®Atom™, Intel Core Duo, Pentium M,
Pentium 4, Intel Xeon, P6 family, Pentium, and Intel486 processors. The
Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4,
IntelXeon, and P6 family processors provide bus control signals that
permit external memory subsystems to make splitaccesses
atomic; however, nonaligned data accesses will seriously impact the
performance of the processor andshould be avoided.
那些被总线带宽、cache line以及page大小给分隔开了的内存地址的访问不是原子的,你如果想保证这些操作是原子的,你就得求助于机制Bus Lock,对总线发出相应的控制信号才行。
An
x87 instruction or an SSE instructions that accesses data larger than a
quadword may be implemented usingmultiple memory accesses. If such an
instruction stores to memory, some
of the accesses may complete (writingto memory) while another causes
the operation to fault for architectural reasons (e.g. due an page-table
entry thatis marked “not present”). In this case, the effects of the
completed accesses may be visible to software
eventhough the overall instruction caused a fault. If TLB invalidation
has been delayed (see Section 4.10.4.4), suchpage faults may occur even
if all accesses are to the same page.
在intel处理器上c++中一些基本的运算那些是基本操作?(本人CPU为Intel
Core i5-4590 3.3GHz, x, y均为int型)
操作 |
反汇编代码(VS2010) |
++i | mov eax,dword ptr [i] add eax,1 mov dword ptr [i],eax |
i++ | mov eax,dword ptr [i] add eax,1 mov dword ptr [i],eax |
x = y | mov eax,dword ptr [y] mov dword ptr [x],eax |
x = 1 | mov dword ptr [x],1 |
x + y | mov eax,dword ptr [x] add eax,dword ptr [y] |
x + 1 | mov eax,dword ptr [x] add eax,1 |
x += y | mov eax,dword ptr [x] add eax,dword ptr [y] mov dword ptr [x],eax |
x += 1 | mov eax,dword ptr [x] add eax,1 mov dword ptr [x],eax |
x - y | mov eax,dword ptr [x] sub eax,dword ptr [y] |
x - 1 | mov eax,dword ptr [x] sub eax,1 |
x -= y | mov eax,dword ptr [x] sub eax,dword ptr [y] mov dword ptr [x],eax |
x -= 1 | mov eax,dword ptr [x] sub eax,1 mov dword ptr [x],eax |
x * y | mov eax,dword ptr [x] imul eax,dword ptr [y] |
x * 10(x*1和x*2会被优化) | mov eax,dword ptr [x] imul eax,eax,0Ah |
x *= y | mov eax,dword ptr [x] imul eax,dword ptr [y] mov dword ptr [x],eax |
x *= 10 | mov eax,dword ptr [x] imul eax,eax,0Ah mov dword ptr [x],eax |
x / y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] |
x / 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx |
10 / x | mov eax,0Ah cdq idiv eax,dword ptr [x] |
x /= y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] mov dword ptr [x],eax |
x /= 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx mov dword ptr [x],eax |
x % y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] |
x % 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx |
x %= y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] mov dword ptr [x],edx |
x %= 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx mov dword ptr [x],edx |
x >> y | mov eax,dword ptr [x] mov ecx,dword ptr [y] sar eax,cl |
x >> 1 | mov eax,dword ptr [x] sar eax,1 |
x << y | mov eax,dword ptr [x] mov ecx,dword ptr [y] shl eax,cl |
x << 1 | mov eax,dword ptr [x] shl eax,1 |
x >>= y | mov eax,dword ptr [x] mov ecx,dword ptr [y] sar eax,cl mov dword ptr [x],eax |
x >>= 1 | mov eax,dword ptr [x] sar eax,1 mov dword ptr [x],eax |
x <<= y | mov eax,dword ptr [x] mov ecx,dword ptr [y] shl eax,cl mov dword ptr [x],eax |
x <<= 1 | mov eax,dword ptr [x] shl eax,1 mov dword ptr [x],eax |
x & y | mov eax,dword ptr [x] and eax,dword ptr [y] |
x & 10 | mov eax,dword ptr [x] and eax,0Ah |
x &= y | mov eax,dword ptr [x] and eax,dword ptr [y] mov dword ptr [x],eax |
x &= 10 | mov eax,dword ptr [x] and eax,0Ah mov dword ptr [x],eax |
x | y | mov eax,dword ptr [x] or eax,dword ptr [y] |
x | 10 | mov eax,dword ptr [x] or eax,0Ah |
x |= y | mov eax,dword ptr [x] or eax,dword ptr [y] mov dword ptr [x],eax |
x |= 10 | mov eax,dword ptr [x] or eax,0Ah mov dword ptr [x],eax |
x ^ y | mov eax,dword ptr [x] xor eax,dword ptr [y] |
x ^ 10 | mov eax,dword ptr [x] xor eax,0Ah |
x ^= y | mov eax,dword ptr [x] xor eax,dword ptr [y] mov dword ptr [x],eax |
x ^= 10 | mov eax,dword ptr [x] xor eax,0Ah mov dword ptr [x],eax |
!x | xor eax,eax cmp dword ptr [x],0 sete al |
~x | mov eax,dword ptr [x] not eax |
从图表中看出就 x = 1为一条汇编操作指令。那我们再来看下不同类型下的的值为x赋值的结果:
类型 | 反汇编代码(vs2010) |
char x = 10; | mov byte ptr [x],0Ah |
unsigned char x = 10; | mov byte ptr [x],0Ah |
short x = 10; | mov eax,0Ah mov word ptr [x],ax |
unsigned short x = 10; | mov eax,0Ah mov word ptr [x],ax |
int x = 10; | mov dword ptr [x],0Ah |
unsigned int x = 10; | mov dword ptr [x],0Ah |
long x = 10; | mov dword ptr [x],0Ah |
unsigned long x = 10; | mov dword ptr [x],0Ah |
long long x = 0xFFFFFFFFFFFF; | mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
unsigned long long x = 0xFFFFFFFFFFFF; | mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
float x = 10.20; | fld dword ptr [__real@41233333 (0F6311Ch)] fstp dword ptr [x] |
double x = 10.20; | fld qword ptr [__real@4024666666666666 (13D3120h)] fstp qword ptr [x] |
__int8 x = 10; |
mov byte ptr [x],0Ah |
__int16 x = 10; |
mov eax,0Ah mov word ptr [x],ax |
__int32 x = 10; |
mov dword ptr [x],0Ah |
__int64 x = 0xFFFFFFFFFFFF; |
mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
C++中线程同步
什么情况需要线程同步
1、有两个或两个以上的线程对同一份数进行操作,而且这些线程中存在写操作。
2、每个线程对该数据的操作完成后才能进行其他线程对该数据的操作,也就是乱序会对数据产生”撕裂“或数据难以预料。
比如说一个结构体重有两个成员变量{int x; int y;}, 如果一个线程对该结构体的实例进行写操作{x = 1, y = 2},另一个也进行写操作{x = 3, y = 4};如果没有进行线程同步,会存在如下几种情况:
x = 1 | y =2 |
x = 1 | y =4 |
x = 3 | y =2 |
x = 3 | y = 4 |
这样会造成数据的”撕裂“,从而需要我们进行线程同步保证数据要么是{x = 1, y = 2},要么是{x = 3, y = 4}。
在C++中面对内建类型的多线程同步,非原子操作并不一定需要线程同步
操作 | 反汇编代码(VS2010) |
说明 |
同步? |
++i | mov eax,dword ptr [i] add eax,1 mov dword ptr [i],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
i++ | mov eax,dword ptr [i] add eax,1 mov dword ptr [i],eax |
同上 | 需要 |
x = y | mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
x = 1 | mov dword ptr [x],1 | 本来就是原子操作 | 不需要 |
x + y | mov eax,dword ptr [x] add eax,dword ptr [y] |
x + y不赋值给其他变量是没有意义的,不做讨论 | |
x + 1 | mov eax,dword ptr [x] add eax,1 |
不赋值给其他变量是没有意义的,不做讨论 | |
x += y | mov eax,dword ptr [x] add eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x += 1 | mov eax,dword ptr [x] add eax,1 mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x - y | mov eax,dword ptr [x] sub eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x - 1 | mov eax,dword ptr [x] sub eax,1 |
不赋值给其他变量是没有意义的,不做讨论 | |
x -= y | mov eax,dword ptr [x] sub eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x -= 1 | mov eax,dword ptr [x] sub eax,1 mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x * y | mov eax,dword ptr [x] imul eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x * 10(x*1和x*2会被优化) | mov eax,dword ptr [x] imul eax,eax,0Ah |
不赋值给其他变量是没有意义的,不做讨论 | |
x *= y | mov eax,dword ptr [x] imul eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x *= 10 | mov eax,dword ptr [x] imul eax,eax,0Ah mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x / y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x / 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx |
不赋值给其他变量是没有意义的,不做讨论 | |
10 / x | mov eax,0Ah cdq idiv eax,dword ptr [x] |
不赋值给其他变量是没有意义的,不做讨论 | |
x /= y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x /= 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x % y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x % 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx |
不赋值给其他变量是没有意义的,不做讨论 | |
x %= y | mov eax,dword ptr [x] cdq idiv eax,dword ptr [y] mov dword ptr [x],edx |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x %= 10 | mov eax,dword ptr [x] cdq mov ecx,0Ah idiv eax,ecx mov dword ptr [x],edx |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x >> y | mov eax,dword ptr [x] mov ecx,dword ptr [y] sar eax,cl |
不赋值给其他变量是没有意义的,不做讨论 | |
x >> 1 | mov eax,dword ptr [x] sar eax,1 |
不赋值给其他变量是没有意义的,不做讨论 | |
x << y | mov eax,dword ptr [x] mov ecx,dword ptr [y] shl eax,cl |
不赋值给其他变量是没有意义的,不做讨论 | |
x << 1 | mov eax,dword ptr [x] shl eax,1 |
不赋值给其他变量是没有意义的,不做讨论 | |
x >>= y | mov eax,dword ptr [x] mov ecx,dword ptr [y] sar eax,cl mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x >>= 1 | mov eax,dword ptr [x] sar eax,1 mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x <<= y | mov eax,dword ptr [x] mov ecx,dword ptr [y] shl eax,cl mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x <<= 1 | mov eax,dword ptr [x] shl eax,1 mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x & y | mov eax,dword ptr [x] and eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x & 10 | mov eax,dword ptr [x] and eax,0Ah |
不赋值给其他变量是没有意义的,不做讨论 | |
x &= y | mov eax,dword ptr [x] and eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x &= 10 | mov eax,dword ptr [x] and eax,0Ah mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x | y | mov eax,dword ptr [x] or eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x | 10 | mov eax,dword ptr [x] or eax,0Ah |
不赋值给其他变量是没有意义的,不做讨论 | |
x |= y | mov eax,dword ptr [x] or eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x |= 10 | mov eax,dword ptr [x] or eax,0Ah mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x ^ y | mov eax,dword ptr [x] xor eax,dword ptr [y] |
不赋值给其他变量是没有意义的,不做讨论 | |
x ^ 10 | mov eax,dword ptr [x] xor eax,0Ah |
不赋值给其他变量是没有意义的,不做讨论 | |
x ^= y | mov eax,dword ptr [x] xor eax,dword ptr [y] mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
x ^= 10 | mov eax,dword ptr [x] xor eax,0Ah mov dword ptr [x],eax |
存在对同一数据的读写, 乱序会造成数据异常 | 需要 |
!x | xor eax,eax cmp dword ptr [x],0 sete al |
不赋值给其他变量是没有意义的,不做讨论 | |
~x | mov eax,dword ptr [x] not eax |
不赋值给其他变量是没有意义的,不做讨论 |
对内建类型变量的赋值(数值):
类型 | 反汇编代码(vs2010) | 说明 |
同步?
|
char x = 10; | mov byte ptr [x],0Ah | 原子操作 | 不需要 |
unsigned char x = 10; | mov byte ptr [x],0Ah | 原子操作 | 不需要 |
short x = 10; | mov eax,0Ah mov word ptr [x],ax |
只有一个写操作 | 不需要 |
unsigned short x = 10; | mov eax,0Ah mov word ptr [x],ax |
只有一个写操作 | 不需要 |
int x = 10; | mov dword ptr [x],0Ah | 原子操作 | 不需要 |
unsigned int x = 10; | mov dword ptr [x],0Ah | 原子操作 | 不需要 |
long x = 10; | mov dword ptr [x],0Ah | 原子操作 | 不需要 |
unsigned long x = 10; | mov dword ptr [x],0Ah | 原子操作 | 不需要 |
long long x = 0xFFFFFFFFFFFF; | mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
写写操作 | 需要 |
unsigned long long x = 0xFFFFFFFFFFFF; | mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
写写操作 | 需要 |
float x = 10.20; | fld dword ptr [__real@41233333 (0F6311Ch)] fstp dword ptr [x] |
只有一个写操作 | 不需要 |
double x = 10.20; | fld qword ptr [__real@4024666666666666 (13D3120h)] fstp qword ptr [x] |
只有一个写操作 | 不需要 |
__int8 x = 10; |
mov byte ptr [x],0Ah |
原子操作 | 不需要 |
__int16 x = 10; |
mov eax,0Ah mov word ptr [x],ax |
只有一个写操作 | 不需要 |
__int32 x = 10; |
mov dword ptr [x],0Ah |
原子操作 | 不需要 |
__int64 x = 0xFFFFFFFFFFFF; |
mov dword ptr [x],0FFFFFFFFh mov dword ptr [ebp-8],0FFFFh |
写写操作 | 需要 |
对内建类型变量的赋值(变量):
类型 | 反汇编代码(vs2010) | 说明 |
同步?
|
char x = y; | mov al,byte ptr [y] mov byte ptr [x],al |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
unsigned char x = y; | mov al,byte ptr [y] mov byte ptr [x],al |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
short x = y; | mov ax,word ptr [y] mov word ptr [x],ax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
unsigned short x = y; | mov ax,word ptr [y] mov word ptr [x],a |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
int x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
unsigned int x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
long x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
unsigned long x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
long long x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax mov ecx,dword ptr [ebp-18h] mov dword ptr [ebp-8],ecx |
对x存在写写操作,乱序会产出数据”撕裂“ | 需要 |
unsigned long long x = y; | mov eax,dword ptr [y] mov dword ptr [x],eax mov ecx,dword ptr [ebp-18h] mov dword ptr [ebp-8],ecx |
对x存在写写操作,乱序会产出数据”撕裂“ | 需要 |
float x = y; | fld dword ptr [y] fstp dword ptr [x] |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
double x = y; | fld qword ptr [y] fstp qword ptr [x] |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
__int8 x = y; |
mov al,byte ptr [y] mov byte ptr [x],al |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
__int16 x =y; |
mov ax,word ptr [y] mov word ptr [x],ax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
__int32 x =y; |
mov eax,dword ptr [y] mov dword ptr [x],eax |
非对同一数据的读写,而且乱序不会对数据结果产生影响 | 不需要 |
__int64 x =
y; |
mov eax,dword ptr [y] mov dword ptr [x],eax mov ecx,dword ptr [ebp-18h] mov dword ptr [ebp-8],ecx |
需要 |