1. 程式人生 > >原子指令於Lock-Free資料結構教學筆記

原子指令於Lock-Free資料結構教學筆記

原文:http://faculty.ycp.edu/~dhovemey/spring2011/cs365/lecture/lecture20.html

示例程式碼:AtomicInstructions.zip

原子指令

原子指令是特殊的硬體指令,以不可分的方式對一個或多個記憶體位置執行操作。無論其他處理器執行什麼指令,原子操作都會成功或完全失敗。

原子指令可以用來做同步處理。由於原子指令可用於更改共享資料而無需獲取和釋放鎖,因此可以實現更高的並行性。但是,由於它們是低層的,並且只能對資料結構進行小的更新,因此使用它們來實現並行資料結構是一項艱鉅的任務。

原子指令的例子:

  • 原子增量 atomic increment

  • 原子交換暫存器和記憶體位置 atomic exchange register and
    memory location

  • 比較與交換 compare and swap

比較與交換

比較與交換(CAS)通常用作無鎖資料結構的基元。它的行為由以下C函式描述,其中“ATOMIC {…}表示以原子方式執行的程式碼塊:

int compareAndSwap(int * loc,int expectedValue,int valueToStore){
        ATOMIC {
                int val = * loc;
                if(val == expectedValue){
                        * loc = valueToStore;
                }
                返回;
        }
}

(上面的函式假定記憶體位置的內容是一個整數,但它可以是任何原始資料型別,包括指標型別。)

Java中的原子指令

java.util.concurrent.atomic中封裝 表示記憶體位置的資料型別,其記憶體位置可以通過原子機器指令訪問(如果主機CPU具有所需的硬體指令)。
這些資料型別類似於內建的“wrapper”資料型別,例如java.lang.Integer,它們用於定義封裝原始值的物件。

AtomicInteger是具有原子操作的共享整數,AtomicReference是具有原子操作的共享引用(指標)等。

應用

共享計數器
原子增量操作可用於實現共享計數器。
偽隨機數生成器


線性同餘偽隨機數生成器 (如java.util.Random)的工作原理是使用遞推以生成基於初始種子值的一系列整數的值。每次呼叫者想要獲得序列的下一個值時,生成器必須

  1. 獲取當前種子值
  2. 使用遞推方程生成下一個種子
  3. 儲存更新的種子值
  4. 返回下一個種子值派生的值

步驟1-3必須以原子方式執行,因為當不同的執行緒同時請求序列的下一個成員時,它們不能返回相同的成員。可以使用互斥鎖確保原子性,但如果許多執行緒同時使用生成器,則產生的競爭可能會降低效能(因為執行緒爭用互斥鎖。)步驟1-3必須以原子方式執行,因為當不同的執行緒同時請求序列的下一個成員時,它們不能返回相同的成員。可以使用互斥鎖確保原子性,但如果許多執行緒同時使用生成器,則產生的競爭可能會降低效能(因為執行緒爭用互斥鎖。)
CAS操作可用於實現樂觀併發。完成上述演算法中的步驟1-2而不獲取鎖。僅當沒有其他執行緒更改它時,步驟3才使用CAS操作來儲存更新的下一個種子值 。如果CAS成功,則執行步驟4並完成操作。如果CAS失敗,因為另一個執行緒更新了種子,則操作返回到步驟1並再次嘗試。
佇列資料結構
這是一篇描述使用CAS操作而不是互斥鎖實現的佇列資料結構的文章:Michael,Maged和Scott,Michael。 簡單,快速,實用的非阻塞和阻塞併發佇列演算法,PODC 1996。
這些演算法也在Michael Scott的網站上作為虛擬碼:
http://www.cs.rochester.edu/research/synchronization/pseudocode/queues.html
本文還描述了一種基於鎖的佇列演算法,其中使用單獨的鎖來保護佇列的頭部和尾部,允許並行和出列操作並行進行。