可能重複:
Are incrementers/decrementers (var++, var--) etc thread safe?爲什麼i ++在一臺核心機器上不是線程安全的?
你能描述我,在彙編代碼級別,爲什麼增加來自兩個不同的線程的值不被認爲是安全的單核的機器上?
可能重複:
Are incrementers/decrementers (var++, var--) etc thread safe?爲什麼i ++在一臺核心機器上不是線程安全的?
你能描述我,在彙編代碼級別,爲什麼增加來自兩個不同的線程的值不被認爲是安全的單核的機器上?
請考慮可能爲i++
之類的語句生成的說明。當然,這將取決於你的架構/指令集,但它可能會沿着線的東西:
LOAD @i, r0 ;load the value of 'i' into a register from memory
ADD r0, 1 ;increment the value in the register
STORE r0, @i ;write the updated value back to memory
現在考慮如何多線程會在操作系統中實現,不管有多少個核的機器具有。在最基本的層面上,操作系統將需要一些工具來中斷當前線程的執行,保存其狀態,並執行上下文切換到另一個線程。操作系統沒有自動的方式來知道用戶線程中的哪些指令應該被視爲原子操作,並且能夠在任何兩條指令之間啓動上下文切換。
那麼如果OS在LOAD
和ADD
之間執行從一個線程到另一個線程的上下文切換,會發生什麼情況?假設i
的值爲0,因此r0
將在第一個線程換出時設置爲0。操作系統會將該值保存爲該線程狀態的一部分。現在第二個線程運行,並執行相同的語句LOAD
。內存中的值仍然爲0,因此r0
將再次裝入0。線程遞增該值並將其寫回內存,將i
的值設置爲1.現在,第一個線程恢復執行,並且作爲其上下文切換的一部分,操作系統將r0
的值恢復爲0。第一個線程現在執行其增量,將r0
設置爲1,並將值1再次存儲在i
中。現在i
的值不正確,因爲已應用了兩個增量,但值只增加了1.
因此,簡而言之,即使i++
是高級語言中的單個語句,它會生成多個程序集這些指令不會被操作系統/運行時環境視爲原子操作,除非您在它們周圍添加額外的同步邏輯。
線程一讀舊的值
定時器中斷熄滅
內核恢復線程2
線程2讀取舊值
線程兩個增量它
線程兩個寫它
計時器熄滅
內核線程恢復1
線程一個增量
線程一個商店
現在你是一個落後。
您的問題標記爲彙編程序,但詢問i ++。您不能保證C代碼中的i++
將編譯爲改變內存的單條指令。如果您有多條線程從一條指令的內存中加載i
,則將其與另一條指令一起遞增,並用第三條指令將其寫回內存,第一條和第三條之間的線程切換可能會導致對i
的某些更新丟失。
阻止系統在讀取值和寫入值的時間之間對一個線程進行重新調度?當然,這種情況發生的可能性較小,但在標準操作系統上,內核可以隨時獲得中斷,並確定另一個線程值得運行。在那一點上,兩個線程都會讀取相同的值,並且兩個線程的增量相同。但是,第二個線程可以運行另一個時間片,遞增數千次,然後當第一個線程重新計劃時,通過寫入陳舊值來阻止第二個線程的所有前進進程。
如果處理器沒有一個單一的指令,可以增加一個內存位置的內容,編譯器將不得不做這樣的事情發生:
load location, registerA
increment registerA
store registerA, location
所以,即使任何一個指令是原子時,序列不是。並且即使有一個單獨的指令,編譯器也不能保證它的使用。例如,編譯器可能已經做了一些優化,並且正在使用寄存器來保存一些常用值,只有在編譯器語言內存模型中的任何排序規則要求的時候纔將其存回內存。
無法預測從單個內核上的2個線程執行的指令序列。以下是可能的順序中,當兩個線程試圖做我++,但效果相當於做我++一次:
load i # thread 1
system interrupt
load i # thread 2, now i++ in thread 1 is not complete
increment i # thread 2
store i # thread 2
system interrupt
increment i # thread 1, actually the same value
store i
謝謝@aroth。我對當操作系統恢復第一個線程時是否有點困惑,它會使R0的內容無效並從內存中重新讀取。現在我很清楚,無論R0的值是多少,無論其他線程可能做了什麼,R0都是。 – Dejas 2011-05-19 01:40:33
在x86上,這實際上可以在單個指令中完成,例如「inc [eax]」,其中eax是指向包含「i」的存儲器的寄存器。這假設「我」甚至存儲在內存中(沒有優化到僅寄存器),並且編譯器選擇使用「inc」指令。因此,即使在單指令內存遞增的架構上,您也不能依賴這一點。 – 2011-05-19 01:47:44