这里用 windows 的 Interlocked系列函数来举例
一直很迷惑Interlocked系列的原子操作函数到底有那些特性,之前只以为原子操 就是保证一个操作的完整性
比如一个i++ 分3步,先取原值 再+1 然后 存回去,cpu执行其中一步后可能就去执行其他指令了.
像是不是2个 Interlocked 对同一个变量的操作 是不是互斥(就是其中一个在执行,另一个需要等待),
Interlocked这句代码 跟它上文代码 的执行顺序是不是一定能保证,一直很迷惑。msdn上面的文档也看了几遍,一直不是很明白
后来才发现自己没看懂。
总结下相关知识点:
首先会导致并发编程的问题其中一些有:
1.多个线程同时操作一块内存
可能会出现一个线程写到一半,另一个读到了写了一半的值
2.一个操作会被分成多个指令执行
比如 i++,这个会产生指令执行的空隙,
3.指令的乱序执行:编译器的代码优化,cpu的指令执行优化,会导致指令执行的乱序,
这种是否进行乱序优化的考虑只会针对当前线程的代码逻辑,当2部分操作 没有存在显性的关系,就可能被编译器或者cpu 优化成 乱序执行
比如 :
线程A
data = XXX // 操作 1
flag = true // 操作 2
线程 B
if (flag)
{
data ...
}
编译器/cpu 可能对 操作1 ,操作2的执行顺序进行优化,操作2 可能被优先执行,这时候 线程B的 逻辑就出现问题
Interlocked 主要解决以上3个问题
Interlocked特性:
1.操作的整体性
比如i++操作,需要3步操作指令,Interlocked能保证这3步连续性,不被中断,要吗联系执行完,要吗不开始执行,不会出现执行第一步,被切换去执行其他指令, 请看翻译第4条
2.操作的互斥性:
对同一块内存操作是互斥,只能依次进行操作,一个Interlocked在操作当前变量,另一个线程的Interlocked就需要等待,请看翻译第5,6条
3.Interlocked 跟Interlocked上下文代码 执行顺序的保证
Interlocked带有内存屏障机制memory barrier,memory barrier用来保证内存操作的顺序,memory barrier的特性请看第7条
通过翻译了,理解了部分相关的文档说明,得出Interlocked以上特性:
(以下翻译加了一些自己的理解)
https://msdn.microsoft.com/en-us/library/windows/desktop/ms684122(v=vs.85).aspx
https://en.wikipedia.org/wiki/Memory_barrier
1.Simple reads and writes to properly-aligned 32-bit variables are atomic operations. In other words, you will not end up with only one portion of the variable updated; all bits are updated in an atomic fashion. ,
简单的读写(不使用interlocked函数)一个内存对齐32位的变量是能保证是原子操作,这里原子操作的意思是:这个32位的内存里面的值,不会出现只更新部分的时刻,也就是内存对齐32位变量的多个操作是依次进行的,然而内存不对齐就不能保证其原子性,因为可能需要2次读/写, 一个线程可能在读/写第一部分,另一个可能在读/写第二部分
ps:这里有个cpu总线读写机制,cpu一次只读/写固定带宽的内存,比如一次读128位,起始从0开始,在机制cpu只会按固定增量的起始位置去读/写,(n-1)*128 到 n*128 - 1,n从1开始。
所以要读/写从126 到 158内存块地址,就需要进行2次读/写操作, 把0-127 和 128-255都读/写过来,才能完成126-158内存块的读/写
2.However access is not guaranteed to be synchronized. If two threads are reading and writing from the same variable, you cannot determine if one thread will perform its read operation before the other performs its write operation.
然而,简单的读写访问不能保证是同步的,也就是不能保证A线程在读这个变量的时候,B线程一定写完
3.Simple reads and writes to properly aligned 64-bit variables are atomic on 64-bit Windows. Reads and writes to 64-bit values are not guaranteed to be atomic on 32-bit Windows. Reads and writes to variables of other sizes are not guaranteed to be atomic on any platform.
简单的读写(不使用interlocked函数)一个内存对齐64位的变量在64位的windows系统上能保证是原子操作, 在32位系统上则不能保证, 其他大小的 读写操作在任何平台上都不能保证是原子的
4.The InterlockedIncrement and InterlockedDecrement functions combine the steps involved in incrementing or decrementing a variable into an atomic operation.
InterlockedIncrement and InterlockedDecrement, 把一个多步+操作或者一个-操作的 整合成一个 原子操作
5.This feature is useful in a multitasking operating system, in which the system can interrupt one thread's execution to grant a slice of processor time to another thread.
这个特性非常有用在多任务操作系统上, 在该系统上一个正在执行的线程能被打断,然后把处理器时间片让给其他线程,
6.Without such synchronization, two threads could read the same value, increment it by 1, and store the new value for a total increase of 1 instead of 2. The interlocked variable-access functions protect against this kind of error.
没有该原子操作的同步机制,2个线程都对一个变量做 +1 操作, 结果可能还是只加了1 而不是 2, 因为一个+1 操作分多步,先取原值, 再加+1,再存回去, 因为时机的关系 两个线程取值的时候可能都是 0, 各自加1以后 存回去还是1,实际上写程序的希望是加2。 interlocked能保证 +1操作的原子性,对同一个变量的内存 只能依次完成操作, 这样就达到 简单读写对齐的32位内存变量 能保证原子的情况,
7.A memory barrier, also known as a membar , memory fence or fence instruction, is a type of barrier instruction that causes a central processing unit(CPU) or compiler to enforce an ordering constraint on memory operations issued before and after the barrier instruction. This typically means that operations issued prior to the barrier are guaranteed to be performed before operations issued after the barrier.
https://en.wikipedia.org/wiki/Memory_barrier
内存屏障指令 能 让 cpu 或者编辑器 强制 约束 屏障指令前后的内存操作的顺序, 这就是说:屏障指令前的内存操作 一定能保证先执行完 相对于屏障指令后的内存操作
(换个说法就是:一定能保证 先执行完屏障指令前的内存操作,再执行屏障指令后的内存操作。 )