2009-06-22 84 views
26

我正試圖從網站上提供的API和文檔學習Clojure。我對Clojure中的可變存儲有點不清楚,我想確保我的理解是正確的。請讓我知道是否有任何我錯誤的想法。Clojure可變的存儲類型

編輯:我正在更新這個,因爲我收到了關於其正確性的評論。


免責聲明:所有這些信息是非正式的和潛在的錯誤。不要使用這篇文章來了解Clojure的工作原理。


瓦爾總是包含一個根結合和可能的每線程結合。它們與命令式語言中的常規變量相當,不適合在線程之間共享信息。 (感謝亞瑟Ulfeldt)

參考文獻被所在地支持,可以改變在單個事務中的任何數目的參考文獻的狀態原子事務的線程之間共享。在退出同步表達式(dosync)時進行事務處理,並使用STM魔法自動解決衝突(回退,隊列,等待等)。調度獨立的行動功能來改變代理的狀態。代理程序立即返回,因此是非阻塞的,儘管代理程序的值在調度函數完成之前未設置。

原子是可以在線程之間同步共享的位置。它們支持不同線程之間的安全操作。

下面是我總結的友好基於何時使用這些結構:

  • 瓦爾像在命令式語言普通的舊變量。 (儘可能避免)
  • 原子就像瓦爾,但線程共享安全,允許立即閱讀和安全設置。 (謝謝馬丁)
  • 一個代理就像一個Atom,而不是阻塞它產生一個新的線程來計算它的值,只有在更改值的過程中阻塞,並且可以讓其他線程知道它已完成分配。
  • 參考是共享位置,將自己鎖定在交易中。我們只是啓動一個事務,讓Clojure處理該事務中引用的所有鎖定條件,而不是讓程序員決定在競爭條件下發生的每一段鎖定代碼。

而且,相關的概念是功能future。對我來說,似乎未來的對象可以被描述爲一個同步代理,其中在計算完成之前根本無法訪問該值。它也可以被描述爲一個無阻塞的Atom。這些準確的未來的概念?

回答

5

這聽起來像你真的得到Clojure!好工作:)

Vars在所有線程中都有一個「根綁定」,並且每個單獨的線程都可以更改它看到的值,從而影響其他線程。如果我的理解是正確的,那麼var就不能存在於一個線程中,而只有一個根用戶綁定對所有用戶都是可見的,它不能被「反彈」,直到它第一次被定義爲(def ...)。

Refs在包含更改的(dosync ...)事務結束時提交,但只有當事務能夠以一致狀態完成時纔會執行。

+0

之前,你很清楚地解釋這些修正,謝謝!根綁定的想法讓我感到困惑,因爲我沒有看到它的任何優勢。我認爲這是因爲更多的技術原因,例如讓可變變量離開堆棧,所以它們不會被釋放。 – Kai 2009-06-22 17:27:15

3

我發現你的問題的兩個問題。

你說:當一個動作發生時則不會返回的值,直到操作完成

http://clojure.org/agents

如果代理訪問說:

代理的狀態總是立即可用於任何線程讀取

即,您永遠不必等待獲取代理的價值(我認爲通過操作更改的值會自動代理並更改)。

deref -method一個Agent的代碼看起來是這樣的(SVN修訂1382):

public Object deref() throws Exception{ 
    if(errors != null) 
    { 
     throw new Exception("Agent has errors", (Exception) RT.first(errors)); 
    } 
return state; 

}

無阻擋參與。

另外,我不明白你的意思是什麼(在你的參考文獻部分)

事務被提交上調用DEREF

事務提交時dosync塊的所有行動已經完成,沒有發生任何異常,也沒有任何事件導致交易重新進行。我認爲deref與它無關,但也許我誤解了你的觀點。

+0

嗨pmf,我覺得你對代理感到困惑。代理的*狀態*總是立即可用,但是在調用發送操作函數完成後,其值將由Clojure異步更改。在我的解釋中,我只是指代理人派遣的最後一步,如下所示:「5.如果在執行功能期間發生任何其他派遣(直接或間接),它們將一直持續到代理人的狀態改變「。這是這裏發生的唯一阻塞。 – Kai 2009-06-22 21:42:52

+0

我關於提交交易的聲明是基於我從Rich Hickley讀到的幻燈片的內存,儘管我可能是錯的。對我來說,在dosync之後提交也更有意義。 – Kai 2009-06-22 21:45:34

4

我覺得你對原子的結論是錯誤的:

原子就像瓦爾但線程共享的安全,阻止,直到該值已更改

原子被swap!或改變低與compare-and-set!。這從不阻止任何事情。swap!的作品就像一個交易只有一個裁判:

  1. 舊值從原子和存儲線程本地
  2. 功能被應用到舊值,以產生新的價值
  3. 如果這成功比較和設置被稱爲新舊值;只有當原子的值沒有被任何其他線程改變(仍然等於舊值)時,新值被寫入,否則操作在(1)處重新開始,直到最終成功。
1

馬丁對他說原子操作在1時重新啓動直到最終成功。 它也被稱爲旋轉等待。 儘管注意確實阻塞了鎖,但執行操作的線程被阻塞,直到操作成功,因此它是阻塞操作而不是異步操作。

另外關於期貨,Clojure 1.1增加了承諾和期貨的抽象。 promise是一個同步構造,可用於將值從一個線程傳遞到另一個線程。在價值交付之前,任何解除承諾的嘗試都將被阻止。

(def a-promise (promise)) 
(deliver a-promise :fred) 

期貨代表異步計算。它們是讓代碼在另一個線程中運行並獲得結果的一種方式。

(def f (future (some-sexp))) 
(deref f) ; blocks the thread that derefs f until value is available 
0

變量並不總是具有根綁定。這是合法的,創建一個變量var而不用綁定使用

(def x) 

(declare x) 

試圖評價X有一個值會導致

Var user/x is unbound. 
[Thrown class java.lang.IllegalStateException]