跳轉到內容

Clojure/併發程式設計學習

來自 Wikibooks,開放的書籍,開放的世界
Previous page
Clojure 學習 Next page
Leiningen
併發程式設計

一個 Ref,就像一個 Var,是一個用於儲存另一個物件的儲存單元,但 Ref 的目的卻截然不同,它是用來儲存併發共享資料的。

  1. Ref 只能在稱為 事務 的程式碼塊中修改。
  2. Ref 可以隨時讀取,包括在事務之外。
  3. 在一個事務中,其他執行緒對 Ref 的更改是不可見的。事務會看到 Ref 在事務開始時的值;如果事務本身更改了 Ref 的值,那麼在事務的其餘部分(或直到再次更改值)中會看到該值。
  4. 對 Ref 進行的事務更改,在事務完成並提交之前,其他執行緒是看不到的。
  5. 如果異常導致退出事務,則該事務中的更改將永遠不會提交,因此會丟失。
  6. 如果在事務執行期間,另一個事務成功提交了該事務修改的 Ref,則事務提交將失敗。
  7. 事務會自動重試,直到成功提交。(因此,事務通常應該是無副作用的,以免副作用執行多次。)

實際上,當兩個在時間上重疊的事務修改同一個 Ref 時,第一個完成的事務將成功,而第二個將失敗並重試。因此,共享資料的併發修改被排序成離散的程式碼塊,每個程式碼塊對該共享資料都有一個一致的檢視。

但是,有時您希望 Ref 改變操作無論其他事務如何都能成功:通勤 操作將一個改變操作應用於 Ref,就像正常情況下一樣,改變其在事務剩餘時間內的值,但提交不會因為其他事務對該 Ref 的提交而失敗:如果其他事務沒有提交到 Ref,則該事務的區域性 Ref 值將被提交;否則,事務區域性值將被丟棄,而是將改變操作再次應用,這次應用於新的當前 Ref 值,並將結果值提交。在實踐中,通勤操作應該是可交換的(因此得名):一系列可交換的操作可以以任何順序應用以獲得相同的結果。例如,遞增計數器是可交換的,因此只有遞增計數器的次數很重要,而遞增操作的順序並不重要。


Clipboard

待辦事項
演示一個可交換操作應用於 ref 的示例


與 Var 或 Ref 不同,Agent 始終只是一個單一的引用。Agent 僅透過傳送到佇列的請求進行修改,這些請求線上程池中非同步處理,但保證按接收請求的順序執行。這些請求中作用於代理的狀態是由先前處理的請求建立的狀態(不一定是在請求時建立的狀態,因為當發出新的請求時,早期的請求可能仍在等待)。

Agent 可以隨時讀取,但讀取的值是當前值,而不是由所有待處理請求完成而產生的值。如果您想要“最新”值,您可以等待 代理,這意味著您可以阻塞當前執行緒,直到代理上的所有待處理請求都完成(不包括在此阻塞期間發出的新請求)。

當在事務中發出請求時,請求不會在事務成功提交之前提交到佇列。

華夏公益教科書