打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
原子操作浅谈 | 学步园

http://www.xuebuyuan.com/2198206.html


2014年10月30日 ? 黄专家专栏


简单说,所谓原子操作是指不会被打断的操作,这种”打断”在操作系统层面,一般是指线程间的上下文切换。假设,一个线程对一个共享的变量写入一个 值,那么另一个观察这个变量的线程,要么看到原值,要么看到新值,不会看到一种中间状态,这种中间状态可以简单理解为部分写入(torn write)。

转到程序设计层面,如果你用C/C++写入或者读取一个变量,这个操作是原子的吗?

答案是:不一定。

假设我们有一个 64bit 的共享变量, store 函数表示写入这个变量一个值

1234
uint64 var;void store() {  var = 0x1000000000000001;}

那么 以上的操作,在 32-bit x86 架构上生成的汇编代码可能如下:

123
mov  DWORD PTR var, 2mov   DWORD PTR var + 4, 1ret

明显可以看出,这个赋值操作不是原子的。同样的,我们可以得到读这个 var 的操作也不是原子的。

但是,如果换成 uint32 呢?

12345
uint32 foo;void store() {    foo = 0x80286;}

如果这个 foo 对其到 32-bit 边界,那么操作是原子的,否则这个操作的原子性某些32-bit的平台不一定支持。 这个在 x64 架构同样成立。我们看下面的例子

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
#include <iostream>#pragma pack(2)struct ShardValue {  char a[57];  uint64_t b;};char a;ShardValue var;void* thread_worker(void* k) {  uint64_t* v = (uint64_t*)k;  while (1) {      var.b = *v;   }}void* thread_observer(void* k) {  uint64_t v;  while(1) {      v = var.b;      if (v == 0x1111111100000001 ||          v == 0x4444444400000001 ||          v == 0x6666666600000001 ||          v == 0x2222222200000001 ||          v == 0x3333333300000001 ||          v == 0x5555555500000001) {      } else {          printf("v : %p\n", v);           exit(0);      }     }}int main() {  pthread_t tid_1, tid_2, tid_3, tid_4, tid_5, tid_6;  uint64_t v1 = 0x1111111100000001;  uint64_t v2 = 0x2222222200000001;  uint64_t v3 = 0x3333333300000001;  uint64_t v4 = 0x4444444400000001;  uint64_t v5 = 0x5555555500000001;  uint64_t v6 = 0x6666666600000001;  pthread_create(&tid_1, NULL, thread_worker, &v1);  pthread_create(&tid_2, NULL, thread_worker, &v2);  pthread_create(&tid_3, NULL, thread_worker, &v3);  pthread_create(&tid_4, NULL, thread_worker, &v4);  pthread_create(&tid_5, NULL, thread_worker, &v5);  pthread_create(&tid_6, NULL, thread_worker, &v6);  pthread_t o1, o2, o3, o4, o5, o6;   pthread_create(&o5, NULL, thread_observer, NULL);  pthread_create(&o6, NULL, thread_observer, NULL);  pthread_create(&o1, NULL, thread_observer, NULL);  pthread_create(&o2, NULL, thread_observer, NULL);  pthread_create(&o3, NULL, thread_observer, NULL);  pthread_create(&o4, NULL, thread_observer, NULL);  pthread_join(tid_1, NULL);  pthread_join(tid_2, NULL);  pthread_join(tid_3, NULL);  pthread_join(tid_4, NULL);  pthread_join(tid_5, NULL);  pthread_join(tid_6, NULL);}

测试机是 x86_64 Intel? Core? i5 CPU 760 @ 2.80GHz 4核心

可以得出这样的结果:

v : 0x5555111100000001

查看汇编指令

123456
_Z13thread_workerPv:.......L2:movq  -8(%rbp), %raxmovq  (%rax), %raxmovq  %rax, var+58(%rip)

赋值的汇编指令只有一条,说明在不对齐情况下,最后会生成多条机器指令

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
多线程之私有数据(YC)
pthread的pthread_join()函数理解实验
linux和android端的pthread学习
Linux多线程编程(不限Linux)
Linux——线程编程
线程私有数据(TSD)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服