[英]Dangers of simultaneous write and read of a boolean in a simple situation
我讀過一些類似的問題,但是那里描述的情況更加復雜。
我在堆和兩個線程中都將bool b
初始化為false
。 我確實了解使用bools
進行操作not atomic
,但請bools
閱讀問題。
第一個線程只能將b = true
設置一次,並且不會對其執行任何其他操作。 第二個線程在循環中檢查b
,如果它是true
則執行一些操作。
我是否需要使用一些同步機制(例如互斥體)來保護b
? 如果我不這樣做會怎樣? 使用ints
時,顯然我可以在同時讀寫時獲得任意值。 但隨着bools
剛好有true
和false
,我不介意一次拿到false
,而不是true
。 這是潛在的SIGSEGV
嗎?
數據爭用導致不確定的行為。 就該標准而言,允許采用兼容的實現來進行段錯誤。
實際上,主要的危險是沒有同步,編譯器將在閱讀器循環中觀察到足夠多的代碼,以判斷b
“從不改變”,並優化除值的首次讀取以外的所有值。 之所以可以這樣做是因為,如果它觀察到循環中沒有同步,那么它將知道對該值的任何寫操作都會導致數據爭用。 允許優化程序假定您的程序不會引發未定義的行為,因此可以假定優化程序假設沒有其他線程進行寫操作。
在實踐中將b
標記為volatile
將阻止這種特定的優化,但是即使在volatile
對象上,數據競爭也是不確定的行為。 調用優化器“看不到”的代碼也會在實踐中阻止優化,因為它不知道該代碼是否會修改b
。 當然,對於鏈接時/整個程序優化,與僅編譯時優化相比,優化器看不到的要少。
無論如何,阻止在軟件中進行優化並不能阻止在具有非一致性緩存的系統上的硬件中發生同樣的事情(至少,因此,我聲稱:其他人認為這是不正確的,並且volatile
訪問是讀取/寫入緩存所需的內容。某些實現的行為確實如此)。 如果您要問標准說了什么,那么硬件是否會無限期地向您顯示過時的緩存並不重要,因為行為仍然是不確定的,因此實現可能會破壞您的代碼,而不管這種特殊的優化是什么。打破了它。
您可能會遇到的問題是,我們不知道讀取器線程需要多長時間才能看到更改的值。 如果它們位於具有不同緩存的不同CPU上,則無法保證,除非您使用內存屏障來同步緩存。
在x86上,這是由硬件協議自動處理的,但是在某些其他系統上則不會。
我是否需要使用一些同步機制(例如互斥體)來保護
b
?
如果不這樣做,就意味着數據競爭。 具有數據爭用的程序具有未定義的行為。 該問題的答案與以下問題的答案相同:“您是否希望程序具有明確定義的行為?”
如果我不這樣做會怎樣?
從理論上講,任何事情都可能發生。 這就是未定義行為的意思。 可能發生的最壞的事情是“第二個線程”可能永遠看不到true
值。
編譯器可以假定程序沒有數據爭用(如果程序的行為不是標准定義的,那么表現就好像沒有問題)。 由於第二個線程僅讀取具有值false
的變量,並且不存在影響這些讀取的同步,因此邏輯結論是該值永不變,因此循環是無限的。 (並且某些無限循環在C ++ 11中具有未定義的行為!)
以下是一些替代解決方案:
使用Mutex,上面的其他答案已涵蓋了細節。
考慮使用讀/寫鎖,它將管理/保護同時進行的讀和寫。 pthread庫提供了一個實現:pthread_rwlock_t
根據您的應用程序在做什么,請考慮使用條件變量(pthread lib impl:pthread_cond_t)。 這實際上是從一個線程到另一個線程的信號,它可以讓您刪除while循環和布爾檢查。
使布爾變量為volatile就足夠了(在x86架構上),不需要互斥體:
volatile bool b;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.