行程同步與臨界區間問題

原子性(atomicity)

atomic(原子的)一詞源自希臘文「不可切割」。一個原子操作是指:在所有其他執行緒看來,它要嘛還完全沒發生、要嘛已經徹底完成——不存在任何別人能觀察到的「中途狀態」。想像撥動電燈開關:從房間另一頭看過來,燈不是亮就是暗,沒有人會看到它「撥到一半」。如果 count = count + 1 是原子的,其他執行緒就絕不可能擠進讀取與寫回之間,那麼競爭條件根本不可能發生。

麻煩在於:你在原始碼裡寫的東西,預設幾乎沒有一樣是原子的。像 count++ 這樣的一行,會編譯成好幾條機器指令(讀取、相加、寫回),而 CPU 可能在其中任何兩步之間被中斷,或另一顆核心同時在跑。即使是單一條機器指令,跨核心時也不保證原子,除非硬體特別承諾。所以「原子」是我們必須刻意取得的性質:藉由使用處理器保證以一個不可分割步驟執行的硬體指令(例如測試並設定或比較並交換),或把一段區域包進一把鎖裡——這樣雖然裡面各步驟本身並不原子,整段區域卻表現得彷彿是原子的:在它完成之前其他執行緒都被擋在外面。

原子性是整個同步機制底層的根本概念。競爭條件正是共用資料上原子性的失效;互斥是讓一段多步驟區域變得「實質原子」的技術;而一條原子硬體指令,則是建造鎖與無鎖資料結構所用的、最小的不可分割積木。每當你推理並行正確性時,第一個問題永遠是:這裡哪些操作真的是原子的,哪些只是看起來像原子?

許多語言提供「原子整數」,其累加保證不可分割。把普通的 count++ 換成原子的 fetch_add,就能讓「兩條執行緒計數」的錯誤消失:每一次累加都把讀取、相加、寫回融合成一個不可被打斷的步驟,因此沒有任何更新會被丟失。

原子 = 沒有可被觀察的中途狀態;別人看到的只有「全部完成」或「尚未開始」。

讀寫單一對齊字組往往(但並非普遍)是原子的,切勿想當然耳。而且就算是一次原子寫入,也完全不保證它相對於其他變數的順序——那是另一回事(記憶體順序)。

又稱
atomic operation不可分割性不可分割操作