《多线程程序中操作的原子性》有30个想法

  1. x++可以用汇编来写 : __asm LOCK inc dword ptr[x]
    这样x++只有一条指令。并且是原子操作指令。

    1. @dzh
      Cool!但是对程序员来说易用性不太好,如果用封装好了的atomic operation更“傻瓜”一点。
      谁都不想当傻瓜程序员,但是在开发效率/可读性/可维护性/平台相关性面前傻瓜点还是有优势的 🙂

  2. 我们会发现最终x的值会是1而不是2,因为Thread 1的结果被覆盖掉了,博主,这句话不是很明白~~请指教~~
    意思是说t1 t2都对x做了++,期望是2,但结果是1 ?

    1. 是的。Thread 1的结果被Thread 2的新值(即1)给覆盖掉了,因为Thread2在进行++之前以为x还是0,而不是1。

  3. 很不错的文章; 早点看到会跟好;
    之前调过一个bug; 相邻的两个bitfield用了两把锁分别保护, 结果就悲剧了;

  4. 关于第二个问题“2. 对Bit field(位域)的读写操作是否是线程安全的?”,有点疑问。第二个问题中给出的例子,到底出现什么情况才会出现线程不安全,能不能给具体解释一下。

    1. Hi, 你可以这么理解,你本来是想用锁来保证只有一个线程会对“field”这个bit field进行读写操作,结果事与愿违,另一个线程尽管不能直接对“field”进行读写操作,但是它却可以对“counter”进行读写,并且在对counter进行读写的时候会改变field的值(即间接对field进行了操作),因为这两个bit field同属于一个word。所以必须用一把锁把这两个同时锁起来才行,这样同一时间绝对只能有一个线程对它俩进行操作了。

    2. 假设位域是:
      struct foo {
      int flag : 1;
      int counter : 15;
      };
      对flag或者counter的读写并不是一个单独的指令就能完成的,而是要经过很多个步骤,才能从一个字(16位)里面解析出flag和counter,计算过程中需要寄存器作为中间值,如果多个线程同时做访问,必然会导致数据的混乱,可以写如下代码,然后反汇编看看就知道了:
      foo f;
      f.flag = 1;
      f.counter = 1;
      GoodLuck:)

  5. x++如果有三条指令,你用硬件的lock前缀搞的定么?恐怕要用软件锁了

    单个指令也无法保证原子性,单个指令才用硬件的lock前缀搞定

    x++可以用汇编来写 : __asm LOCK inc dword ptr[x]
    这样x++只有一条指令。并且是原子操作指令

  6. 你好,你的这篇文章让我获益匪浅,但有以个疑问:
    16-bit accesses to uncached memory locations that fit within a 32-bit data bus(未缓存且在32位数据总线范围之内的内存地址的访问)
    我的理解是假设有一个地址X是32位对齐的,那么如果我要访问X+8~X+24,这16位的数据,虽然X+8不是16位对齐的,但没有越过这个32位边界,所以是有原子保证的,请问我的理解是否正确?如果不正确,能否说明一下,感谢万分:)

  7. 那个x++和++x的问题:在time 2的时候,cpu分别对eax中的值做了两次add操作,这样不就从0变到2了么?

  8. 你考虑的太少了, CPU之外, 编译器也需要考虑. GCC的编译优化中, 有些时候会把赋值操作给优化掉, 这样你就无法保证C语言层面上的赋值操作是原子的. volatile关键字是很重要的, 必须加上这个关键字才能保证”赋值”这个操作的原子性.

  9. time Thread 1 Thread 2
    0 load eax, x
    1 load eax, x
    2 add eax, 1
    3 add eax, 1
    4 store x, eax
    5 store x, eax

  10. 指出一个问题,在讲位域那一节,X86 能操作的最小内存单元不是 16 bits, 而是1 byte,即8bits, 基本上常用的cpu,一个地址所对应的内存单元都是1字节,只有DSP 除外。如果最小的单元是16bits,那8位的char 类型有毛意义 (注:DSP 下的char才是16位)

电子邮件地址不会被公开。 必填项已用*标注