"Loads are not reorderd with other loads" is a FACT!!
對于多線程編程的難度,再充分的心里準備也許都是不夠的。前一段時間一直在整理一些有關(guān)多線程編程的內(nèi)容(一個對多線程算法庫編寫過程中的經(jīng)驗積累)。而在前天,一篇來自于Microsoft 的PFX的Joe的博文驚現(xiàn):"Loads cannot pass other loads" is a ~myth,著實讓人驚出了一身冷汗。
討論集中在了以下的例子上:
P0 P1
========== ==========
X = 1; Y = 1;
R0 = X; R2 = Y;
R1 = Y; R3 = X;
問:如果X, Y 為 volatile 有沒有可能使得執(zhí)行完畢之后 R1 == R3 == 0 呢?
先不說結(jié)果,從現(xiàn)象分析,具體信息請參考。首先,在.NET 的內(nèi)存模型下,任何store均有relase語義而任何volatile的load均有acquire語義。由于相關(guān),R0 = X 不可能調(diào)整到 X = 1 之前執(zhí)行;同理 R2 = Y 也不可能調(diào)整到 Y = 1 執(zhí)行。參考Intel 白皮書中的2.1節(jié)可知,“loads are not reordered with other loads and stores are not reorderd with other stores”。因此 R1=Y也不可能移動到R0=X之前,同理,R3=X也不能移動到R2=Y之前。
綜上所述,想要得到R1==R3==0的結(jié)果看上去是不可能的。但是這個事情確實有可能發(fā)生。參考Intel的白皮書中的2.4節(jié):“intra-processor forwarding is allowed”恰恰舉了相同的例子,并且指出,R1==R3==0是完全可能的。Joe指出,在這種情況下,程序就好像是這樣運作的:
P0 P1
========== ==========
R1 = Y; R3 = X;
X = 1; Y = 1;
R0 = X; R2 = Y;
這是怎么一回事呢,難道自相矛盾嗎?實際上不是的!白皮書中2.1節(jié)說明的情況是memory reorder,而2.4節(jié)并沒有否定2.1的內(nèi)容。內(nèi)存訪問的reorder規(guī)則僅僅滿足于當前的processor,而并不保證所有的結(jié)果其余的processor可見。這是由于寫入延遲造成的。因此這個問題并沒有否定在上述例子中reorder不可能發(fā)生的事實,而是對于其他處理器來說“好像是”發(fā)生了reorder一樣。
load acquire與store release實際上看做不完整的half fence,不能保證其他CPU的可見性。那么這種問題如何解決呢?參考Intel 64 And IA-32 Programming Manual可知,如果要保證可見性,應(yīng)該在必要的位置使用memory fence。因此,解決上述問題的方式是在恰當?shù)牡胤教砑?/span>full fence,或者包含隱式full fence的指令,例如Interlocked.Xxx。即
P0 P1
========== ==========
X = 1; Y = 1;
MemoryFence;MemoryFence;
R0 = X; R2 = Y;
R1 = Y; R3 = X;
注:在.NET framework中,插入一個Full fence可以使用 System.Threading.Thread.MemoryBarrier()方法。
對于多線程編程的難度,再充分的心里準備也許都是不夠的。前一段時間一直在整理一些有關(guān)多線程編程的內(nèi)容(一個對多線程算法庫編寫過程中的經(jīng)驗積累)。而在前天,一篇來自于Microsoft 的PFX的Joe的博文驚現(xiàn):"Loads cannot pass other loads" is a ~myth ,著實讓人驚出了一身冷汗。
浙公網(wǎng)安備 33010602011771號