與 Ref 類似,代理提供對可變狀態的共用存取。其中 Ref 支援協調的、同步的多重位置變更,而代理提供獨立的、非同步的個別位置變更。代理會在其生命週期內繫結到單一儲存位置,且僅允許該位置的變異(至新狀態)發生在動作的結果中。動作是函式(選擇性地加上其他引數),會非同步地套用至代理的狀態,且其回傳值會成為代理的新狀態。由於動作是函式,因此它們也可以是多重方法,因此動作具有多態性。此外,由於函式集合是開放的,因此代理支援的動作集合也是開放的,這與其他語言提供的模式比對訊息處理迴圈形成鮮明的對比。
Clojure 的 Agent 是反應式的,而非自主的 - 沒有命令式訊息迴圈,也沒有阻擋接收。Agent 的狀態本身應該是不可變的(最好是 Clojure 的持續性集合實例),而且 Agent 的狀態隨時可供任何執行緒使用(使用 deref 函式或 reader 巨集 @),無需任何訊息,也就是說,觀察不需要合作或協調。
Agent 動作分派採取 (send agent fn args*) 的形式。send(和 send-off)總是立即傳回。稍後在另一個執行緒中,將會發生以下情況
-
給定的 fn 將會套用在 Agent 的狀態和 args(如果有提供的話)。
-
fn 的傳回值將會傳遞給驗證函式,如果 Agent 已設定驗證函式的話。請參閱 set-validator! 以取得詳細資料。
-
如果驗證成功或沒有給定驗證函式,給定 fn 的傳回值將會變成 Agent 的新狀態。
-
如果已新增任何監控器到 Agent,將會呼叫它們。請參閱 add-watch 以取得詳細資料。
-
如果在函式執行期間進行任何其他分派(直接或間接),它們將會保留,直到在 Agent 的狀態變更之後。
如果動作函式擲出任何例外,不會發生巢狀分派,而且例外將會快取在 Agent 本身中。當 Agent 有快取的錯誤時,任何後續互動都會立即擲出例外,直到清除 Agent 的錯誤為止。可以使用 agent-error 檢查 Agent 的錯誤,並使用 restart-agent 重新啟動 Agent。
所有 Agent 的動作會在執行緒池中的執行緒之間交錯。在任何時間點,每個 Agent 最多只會執行一個動作。從另一個單一 Agent 或執行緒分派到 Agent 的動作將會按照它們被傳送的順序執行,可能會與從其他來源分派到同一個 Agent 的動作交錯。send 應使用在受 CPU 限制的動作上,而 send-off 適用於可能會在 IO 上阻擋的動作。
Agent 與 STM 整合 - 在交易中進行的任何分派都會保留,直到它提交,如果它重試或中止,就會捨棄。
與 Clojure 的所有並行支援一樣,不涉及使用者程式碼鎖定。