现在的位置: 首页 > 黄专家专栏 > 正文

原子操作浅谈

2014年10月30日 黄专家专栏 ⁄ 共 2170字 ⁄ 字号 评论关闭

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

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

答案是:不一定。

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

1
2
3
4
uint64 var;
void store() {
  var = 0x1000000000000001;
}

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

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

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

但是,如果换成 uint32 呢?

1
2
3
4
5
uint32 foo;

void store() {
    foo = 0x80286;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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

查看汇编指令

1
2
3
4
5
6
_Z13thread_workerPv:
......
.L2:
movq  -8(%rbp), %rax
movq  (%rax), %rax
movq  %rax, var+58(%rip)

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

抱歉!评论已关闭.

  • 第十六届中国经济论坛 2019-06-26
  • 拜博口腔医疗集团创始人、董事长黎昌仁获第十二届人民企业社会责任奖年度人物奖 2019-06-26
  • 县名解析晋城高平市地名来历 2019-06-25
  • “网络党课”第二课 杨禹《为美好生活而奋斗》 2019-06-25
  • 自然规律是不可改变的,社会规律是可以改变的。这是自然科学与社会科学的区别之一。 2019-06-25
  • 香港有祖国全面支持<br>港人对未来满怀憧憬 2019-06-25
  • 中央第四环保督察组向江西移交1034件信访问题线索 2019-06-24
  • 第十二届中国(南宁)国际园林博览会吉祥物正式发布 2019-06-24
  • C级总销量迫近A4L 宝马3系乏力 2019-06-24
  • 包车司机借口“学炒股”敲开门 抢钱后杀人抛尸 2019-06-23
  • 临汾“尧王杯”马拉松赛激情开跑 2019-06-23
  • 我们包住内力,在不断变化中寻找契机可出击可借力亦可卸力。 2019-06-23
  • “ONE NIGHT 给小孩”北京站探访周迅刘雯共奏可爱“交响曲” 2019-06-22
  • 爱护民生:什么基金都不能买,即使获利,也不会给分多少红利,只是意思意思。 2019-06-22
  • 三颗迄今最年轻行星现形 2019-06-22
  • 五子棋开局6步必胜下法 360捞时时彩开奖 青海快三综合走势图 重庆老时时彩开机号码 腾讯分分彩稳赚规律技巧 007皇家赌场剧情 浙江11选5遗漏走势图 舟山飞鱼和海南飞鱼 博彩网能2元买的 福彩3d追号计算器 6个平码怎推算下期 北京pk10彩票走势图 甘肃11选5今天开奖号码 香港赛马会内幕免费资料 黑龙江时时彩麻将预测