1. 程式人生 > >c/c++語言中的volatile 保證讀寫執行緒安全的條件

c/c++語言中的volatile 保證讀寫執行緒安全的條件

背景: 關於這個問題之前討論了很多, 比如: 

文中大多認為, volatile不能保證執行緒安全, 不是標準操作, 不要使用. 這樣是最保守的說法, 自然沒什麼錯誤, 但是volatile就完全沒用了麼? 本著理論+實踐的做法, 我們看看在什麼情況下, 是可以保證讀寫的執行緒安全的. 

1. volatile定義: 

  • Every access (both read and write) made through an lvalue expression of volatile-qualified type is considered an observable side effect for the purpose of optimization and is evaluated strictly according to the rules of the abstract machine (that is, all writes are completed at some time before the next sequence point). This means that within a single thread of execution, a volatile access cannot be optimized out or reordered relative to another visible side effect that is separated by a 
    sequence point
     from the volatile access.
  • 從這段話, 我們可以理解為 volatile可以保證可見性的, 也就是說, 讀取的時候不會使用類似CPU的快取導致資料的不一致, 但是它不保證讀寫的原子性, 所以也就不能保證執行緒安全. 那我們是否可以通過CPU來保證原子性呢? 

2. 看下Intel CPU 對原子性的保證:

The Intel486 processor (and newer processors since) guarantees that the following basic memory operations will always be carried out atomically:

    • Reading or writing a byte
    • Reading or writing a word aligned on a 16-bit boundary
    • Reading or writing a doubleword aligned on a 32-bit boundary

The Pentium processor (and newer processors since) guarantees that the following additional memory operations will always be carried out atomically:   

    • Reading or writing a quadword aligned on a 64-bit boundary

    • 16-bit accesses to uncached memory locations that fit within a 32-bit data bus

     就是說, 讀寫單個位元組以及int_16, int_32這類資料的時候, 能保證讀寫的原子性, 而其他CPU至少可以保證單位元組的讀寫原子性

3. 實驗:

    首先參考: http://bbs.chinaunix.net/thread-1805189-1-1.html, 在intel Xeon E5-2620 上驗證通過

    之後自己簡單寫了一個程式: 

int main() {
    int a = 1;
    return a;
}

    對應的彙編程式碼如下:

0000000000400580 <main>:
  400580:	55                   	push   %rbp
  400581:	48 89 e5             	mov    %rsp,%rbp
  400584:	c7 45 fc 01 00 00 00 	movl   $0x1,-0x4(%rbp)
  40058b:	8b 45 fc             	mov    -0x4(%rbp),%eax
  40058e:	5d                   	pop    %rbp
  40058f:	c3                   	retq

     可以看到賦值操作是通過movl指令完成的, 而movl是可以保證原子性的. 

4. 結論: 在intel 486+ 架構中, 我們能使用int_32 + volatile保證讀寫的執行緒安全, 如果是其他CPU架構, 則需要使用char + volatile保證執行緒安全

參考文件: