交易記憶體(transactional memory)
交易記憶體借用資料庫交易的概念,把它帶到一般的記憶體內資料。你不必再小心選擇要取哪些鎖、依什麼順序——那是鎖式程式設計裡最容易出錯的核心——而是單純地把一段程式碼包進「atomic { ... }」,系統便承諾整段看起來一次全部發生:要嘛裡面每個改動一起生效,要嘛全都不生效,且沒有別的執行緒會看到做到一半的狀態。這就像銀行承諾一筆轉帳要嘛完整完成、要嘛完整回滾,沒有錢不見的那一刻——只不過如今這個承諾涵蓋你標記的任何一段程式碼,而不只是資料庫。
常見的實作是樂觀的。交易先推測性地執行,把它讀寫的每個記憶體位置記在一份私有日誌裡,不取鎖。到達結尾時,系統檢查衝突:是否有別的交易修改了這個交易讀過的某個位置?若無,交易便提交——它緩衝的寫入原子地變得可見。若有衝突,交易便中止,把它暫定的改動當作從未發生般丟棄,再重試。它有兩種形式:軟體交易記憶體(STM),純以函式庫或語言執行期實作(如 Haskell 或 Clojure);以及硬體交易記憶體(HTM),由 CPU 的快取追蹤讀寫集合並直接偵測衝突(Intel 的 TSX 是著名的嘗試)。
它的吸引力在於可組合性與易用:你可以孤立地推理每一段原子區塊,兩個正確的交易組合成一個正確的更大交易(這是鎖出了名給不了你的),且沒有會導致死結的鎖排序難題。誠實的現實是,它在實務上並未取代鎖。STM 帶有真實的簿記開銷,並可能在高競爭下活結(交易反覆互相中止);HTM 有硬限制——一個碰太多記憶體、或執行了無法回滾的 I/O 或系統呼叫的交易,根本無法以交易方式執行,必須退回到鎖。Intel 甚至因臭蟲而停用了某些 TSX 實作。交易記憶體仍是個優雅的概念,在少數利基處、以及作為鎖省略的硬體加速器被廣泛使用,但不是它一度承諾的那種並行萬靈丹。
把轉帳當作一筆交易:atomic { balance[A] -= amount; balance[B] += amount; }。要嘛兩個更新一起提交,要嘛若有衝突的交易碰過這些帳戶,整段就中止並悄悄重試——沒有選任何鎖,也不可能發生鎖排序的死結。
把一段程式碼包進 atomic;執行期讓它全有或全無,衝突時重試——沒有鎖排序的難題。
交易記憶體不是並行的完成版解藥:STM 有開銷且可能活結,而 HTM 無法處理太大、或做了像 I/O 這種不可逆工作的交易——那些必須退回到一般的鎖。