一、提出問題:一條CPU原語指令如何保證多條指令的原子性
從上篇文章我們知道當多個CPU訪問(此處訪問的含義不僅有讀取記憶體資料的意思,同時也有往記憶體寫入資料的意思)同一個資料時,CPU存在著穿插執行的行為,從而造成資料紊亂的情況,為此CPU提供了鎖機制來保證資料一致性,鎖機制背后的原理就是通過CPU的一條原子性指令(原語)限制只能有一個CPU執行該指令,但我們的應用程式的某個方法往往是由多條指令(通過匯編器產生的ISA指令集)組成的,那一條原語指令如何保證多條指令的原子性呢?換個問法就是:應用程式的多個執行緒訪問同一個方法時是如何保證執行緒安全的呢?
二、只有設定標志位成功的執行緒才可以執行方法

如圖所示,應用程式有一個方法,該方法內部存在對資料的寫操作,現在有n個執行緒想同時執行該方法,那么勢必會存在多執行緒并發的問題,那么CPU底層是如何通過一條CPU原語指令保證多條指令的原子性呢?答案就是通過引入一個標志位,讓CPU去爭搶設定標志位,這些CPU通過原子性指令(lock;add或者lock;sub)來設定標志位,由于是原子操作,那么只有一個CPU能設定成功,也即只有設定標志位成功的執行緒可以執行方法代碼,設定失敗的其他執行緒則要排隊進入阻塞佇列,
三、設定標志位失敗的執行緒自旋進入阻塞佇列

因為設定標志位失敗的執行緒有多個,它們進入佇列的程序是要拿到隊尾指標,然后把隊尾指標指向自己,那么搶這個隊尾指標的操作,又是一個原子性操作,通過CPU的原子性指令(lock;cmpxchg)來讓多個執行緒排隊進入佇列,這個操作稱之為自旋,
四、喚醒和排隊也是原子性操作
當執行緒執行完代碼后,需要喚醒阻塞佇列中的執行緒繼續執行方法,而在喚醒的同時有可能有其他執行緒在排隊進入佇列中,所以喚醒和排隊這兩個操作也是原子性操作,
五、總結

我們繪制完整執行流程圖,可以清晰看到CPU是如何通過一條CPU原語保證多條指令的原子性,我們可以通過以下偽代碼簡要概述上述邏輯程序:
if(設定標志位){
// 設定標志位成功的執行緒執行方法
} else {
// 設定失敗的執行緒排隊
for(;;){
// 通過CAS進入佇列
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/509594.html
標籤:其他
