Software Transactional Memory

soft transactional memory

1 Software Transactional Memory

原文 編寫併發軟件很困難,由於運行順序無法控制,產生的錯誤很難復現,同時,全面的測試也需要花費更多的精力。 多線程軟件編寫通常有兩種劃分方法,

  • 一種方法是把單一的任務劃分爲多個獨立的小片段,然後每個線程獨立執行各個片段;
  • 另一種方法是協調多個任務同步執行;

顯然,後一種方法更難實現,而且不容易追蹤和差錯。

編寫併發軟件有許多種模型。現在最長用的是:locks, actors, transaction memory。這篇文章簡單介紹了三種模型,然後會深入探討soft-base Transactional Memory 本文的目的有:

  • 介紹STM
  • STM的性能特點給出證明
  • 推薦多種語言對STM模型的支持
  • 推薦對STM感興趣的開發者實現Clojure的STM模型
  • 提供開發者對Clojure中的STM模型足夠的認識,讓他們開發出實用的STM開發測試工具,如跟蹤事務處理次數等。

1.1 Lock-base Concurrency

鎖機制的併發任務,一般多線程訪問共享內存。最簡單的模型爲:在訪問共享內存代碼執行前,首先要獲取互斥鎖,保證同一時間只有一個線程訪問這段共享內存。 優勢:

  • 開發者可以顯示的控制鎖的行爲
  • 許多開發者都很熟悉這一模型
  • 許多開發語言內置支持此模型

劣勢:

  • 很難控制什麼時候需要用鎖
  • 鎖使用異常情況下, 互斥鎖變量能夠被訪問
  • 死鎖
  • 錯誤狀態恢復比較複雜,開發者需要記住釋放互斥鎖
  • 正常的狀態同步方法會組合會成爲複雜的方法,但是卻需要額外的互斥鎖
  • 悲觀鎖。把共享區的訪問建立在同時訪問只有一個線程的基礎上,這並不總是正確的,而且這一假設降低了併發的效率。

1.2 Actor-based Concurrency

Actor Mode機制是另一個併發模式訪問共享內存的解決方法。把Actor視爲一個實體,即一個獨立的進程或者輕量級的線程。 actor不會主動訪問共享內存區域,而是被動的接收異步消息。當一個actor接收到消息後,可以做以下幾種操作:

  • 新建actor
  • 發送消息給其他actor
  • 定義如何處理下一個接收到的消息

Actor-based concurrency的優勢:

  • 由於actors不會訪問共享區域,共享區的訪問不再需要同步

劣勢:

  • 由於actor不會直接訪問共享內存,通過發送消息共享,消息內容會很大
  • 不同的actor持有狀態不同

Actor mode被Erlang, Haskell, Scala所支持

1.3 Transactional Memory

Transactional Memory是另一個併發模式訪問共享的解決方法。這種方法簡化了開發併發軟件的複雜度。 Transactional Memory的概念類似於DB Transactions,提供了ACID幾種特性:

  • "A" is for atomic,指所做的操作完全成功,或者完全不成功。
  • "C" is for consistant,指數據在Transaction期間,從開始到結束都是一致的,不會中途修改。
  • "I" is for isolated,指在Transaction期間修改的數據不會被其他進程所見,只有commit後纔會。
  • "D" is for durable,指一旦commit後,所做修改的數據不會丟失,即使在軟硬件出錯的情況下。

Transactions根據代碼不同有許多不同的實現,在其他不同的線程視角,在做commit操作後,所有的共享內存修改都是同時生效的。但是在 commit之前,其他線程並不能看到所做的修改。這是因爲所有的操作都是在一塊獨立的鏡像區域操作的。例如:Transaction A對一塊 內存區域做了修改,同時Transaction B也在對這塊內存做修改,並且B在A之前做了commit操作,這是A會用B所做修改後的數據重新做之前的 修改操作。這些操作特性保證了Transactional Memory的Atomic,Consistant 和Isolated。注意Transational Memory並不能保證durable 的特性,因爲軟件緩存異常或者硬件異常,都會引起數據的丟失。 Transactional Memory是樂觀的,所有它所修改的數據,它都認爲其他線程沒有修改過,如果被其他線程修改過,那麼它會重新讀取最新的數據, 並在新數據基礎上做修改操作。這些特性保證了所有的transaction操作不會產生不可恢復等副作用。 優勢:

  • 可以增加併發運行的效率
  • 編寫併發軟件更簡單
  • 實現可以保證不會發生deadlock、livelock、race condition的發生

劣勢:

  • 存在許多潛在的transaction retries,這會耗費許多資源
  • 需要緩存許多transaction中間狀態值
  • 分析工具匱乏

1.4 Clojure有4種引用類型:

  • Var,Var擁有和其他線程共享的原值和線程獨有的值。def,set!,binding可以用來修改Var變量值。一般Var變量都是建議用作常量。一種比較 常用的修改Var變量值的是當Var變量作爲配置參數時。
  • Atom,Atom在所有線程中擁有唯一的值。所以對它的訪問都是需要原子性。reset!,compare-and-set!,swap!函數可以用來操作Atom變量。
  • Agent, Agents在所有線程中都是相同的值。修改Agents變量值是通過調用異步函數實現的。發送給相同Agent的動作會排在同一隊列中, 所以同一時間只有一個Agent動作在執行。send和send-off是用來操作Agent變量的,當動作異步執行後,立即會有返回值。這兩個操作函數 唯一的區別是send是固定Queue大小的,而send-off的Queue大小是變化的。await函數會終止當前線程的操作,直到發送的操作全部發送到 指定的Agent;await-for函數唯一的區別是可以設置timeout。Agent的修改動作都是在Transaction內部進行的,只有當commit完成後 纔會返回。這一特點可以用來執行有副作用的transaction,像IO操作等。
  • Ref,Ref在所有線程中擁有相同的值。所有修改操作都在STM Transaction中進行。ref-set,alter,commute幾個是操作函數。

Date: 2013-07-27 22:26:42 中國標準時間

Author:

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章