原子性可以通過機器級指令來實作,例如compare and swap (CS). 它也可以通過使用mutex/lock大塊代碼來實作,作業系統提供幫助。
另一方面,我們也有 的概念memory model。有些機器可能有一個relaxed像 Arm 這樣的模型,它可以在一個執行緒上重新排序加載/存盤,有些機器有一個更嚴格的模型,比如x86.
我想確認我對同步一詞的理解。它是兩個幾乎是承諾atomicity和memory model?即僅在執行緒上使用原子操作不需要使其與其他執行緒同步?
uj5u.com熱心網友回復:
原子的東西是不可分割的。這事情同步在時間在一起發生的事情。
原子性
我喜歡把它想象成一個資料結構,它表示一個帶有 x、y 坐標的二維點。出于我的目的,為了使我的資料被認為是“有效的”,它必須始終是沿 x = y 線的一個點。x 和 y 必須始終相同。
假設最初我有一個點 { x = 10, y = 10 },我想更新我的資料結構,使其代表點 {x = 20, y = 20}。并且假設更新操作的實作基本上是這兩個獨立的步驟:
- x = 20
- y = 20
如果我的實作像那樣分別寫入 x 和 y,那么其他一些執行緒可能會在步驟 1 之后但在步驟 2 之前觀察我的點資料結構資料。如果允許在我更改 x 之后但在我更改之前讀取點的值y 那么其他觀察者可能會觀察到值 {x = 20, y = 10}。
實際上可以觀察到三個值
- {x = 10, y = 10}(原始值)[有效]
- {x = 20, y = 10}(x 已修改但 y 尚未修改)[ INVALID x != y]
- {x = 20, y = 20}(x 和 y 均已修改)[ VALID ]
我需要一種同時更新這兩個值的方法,這樣外部觀察者就不可能觀察到 {x = 20, y = 10}。
當其他觀察者看到我的觀點的價值時,我真的不在乎。它觀察到 { x = 10, y = 10 } 很好,如果它觀察到 { x = 20, y = 20 } 也很好。兩者都具有 x == y 的屬性,這使得它們在我的場景中有效。
最簡單的原子操作
最簡單的原子操作是單個位的測驗和設定。這個操作原子地讀取一個位的值并用 1 覆寫它,回傳我們覆寫的位的狀態。但是我們得到保證,如果我們的操作已經結束,那么我們有我們覆寫的值,并且任何其他觀察者將觀察到 1。如果多個代理同時嘗試此操作,則只有一個代理將回傳 0,其他代理將全部回傳1. 即使是兩個 CPU 在完全相同的時鐘滴答上寫入,電子設備中的某些東西也會保證操作按照我們的規則在邏輯上原子地結束。
這就是邏輯原子性。這就是原子手段。這意味著您有能力在更新之前和之后使用有效資料執行不間斷更新,并且在更新期間可能處于任何中間狀態的其他觀察者無法觀察到資料。它可能是單個位,也可能是整個資料庫。
x86 示例
可以在 x86 上以原子方式完成的一個很好的例子是 32 位互鎖增量。
這里一個 32 位(4 位元組)的值必須增加 1。這可能需要修改所有 4 個位元組才能正常作業。如果要將值從 0x000000FF 修改為 0x00000100,重要的是 0x00 變為 0x00 并且 0xFF 變為 0x00 atomically。否則,我可能會觀察到值 0x00000000(如果首先修改 LSB)或 0x000001FF(如果首先修改 MSB)。
硬體保證我們可以一次測驗和修改 4 個位元組來實作這一點。CPU 和記憶體提供了一種機制,即使有其他 CPU 共享相同的記憶體,也可以通過該機制執行此操作。CPU 可以斷言一個鎖定條件,以防止其他 CPU 干擾這個互鎖操作。
同步
同步只是談論事物如何及時一起發生。在您建議的背景關系中,它是關于我們程式的各個部分執行的順序以及我們系統的各個組件更改狀態的順序。如果沒有同步,我們就有損壞的風險(進入我們的程式或其資料的無效、語意上無意義或不正確的執行狀態)
假設我們想要一個 64 位數字的互鎖增量。讓我們假設硬體并沒有提供一種方法,在一個時間原子改變64位。我們必須用更復雜的資料結構來完成我們想要的,這意味著即使只是讀取我們也不能簡單地分別讀取 64 位數字的最高有效 32 位和最低有效 32 位。我們會冒險觀察 64 位值的一部分與另一半分開變化。這意味著我們在讀取(或寫入)這個 64 位值時必須遵守某種協議。
為了實作這一點,我們需要一個原子測驗和設定位操作以及一個清除位操作。(僅供參考,從技術上講,我們需要的是計算機科學中通常稱為P 和 V 的兩個操作,但讓我們保持簡單。)在讀取或寫入資料之前,我們對單個 (共享)位(通常稱為“鎖”)。如果我們讀到一個零,那么我們知道我們是唯一一個看到零的人,其他人一定看到過 1。如果我們看到一個 1,那么我們假設其他人正在使用我們的共享資料,因此我們別無選擇但只是再試一次。因此,我們回圈并不斷測驗和設定該位,直到我們觀察到它為 0。(這稱為自旋鎖,并且是我們在沒有作業系統調度程式幫助的情況下能做的最好的事情。)
當我們最終看到 0 時,我們可以安全地分別讀取 64 位值的兩個 32 位部分。或者,如果我們正在寫入,我們可以安全地分別寫入 64 位值的兩個 32 位部分。一旦兩半都被讀取或寫入,我們將該位清除回 0,允許其他人訪問。
任何這種巧妙結合使用原子操作來避免以這種方式破壞的行為都構成了同步,因為我們控制著程式某些部分可以運行的順序。只要我們能夠訪問某種原子資料,我們就可以實作任何復雜性和任何數量的資料的同步。
一旦我們創建了一個使用鎖以無沖突方式共享資料結構的程式,我們也可以將該資料結構稱為邏輯原子的。例如,C 提供了一個std::atomic來實作這一點。
請記住,此級別的同步(使用鎖)是通過遵守協議(使用鎖保護您的資料)來實作的。其他形式的同步,例如當兩個 CPU 嘗試在同一時鐘滴答上訪問同一記憶體時發生的情況,由 CPU 和主板、記憶體、控制器等在硬體中解決。但基本上類似的事情正在發生,只是在主板級別。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/409606.html
標籤:
上一篇:子執行緒看不到主執行緒增加的效果
