2011-06-19 126 views

回答

8

如何使用代理而不是鎖來確保這一點?

我認爲使用代理來保護共享可變狀態,無論它在內存中還是在磁盤上比clojure更習慣於使用鎖。

如果您一次創建一個代理程序並將訪問嘗試發送給代理程序,則可以確保只在線程上訪問給定文件。

例如像這樣:

(use 'clojure.contrib.duck-streams) 

(defn file-agent [file-name] 
    (add-watch (agent nil) :file-writer 
    (fn [key agent old new] 
     (append-spit file-name new)))) 

(defn async-append [file-agent content] 
    (send file-agent (constantly content))) 

然後通過代理附加文件:

(async-append "content written to file" (file-agent "temp-file-name")) 

如果你需要它可以用的​​await可以實現文件的同步使用。就像這樣:

(defn sync-append [file-agent content] 
    (await (send file-agent (constantly content)))) 
+0

整潔!花了一點時間纔想起這件事。我最終使用了一個地圖,其中包含每個打開文件的條目。你的解決方案看起來好像更好。 – jhickner

+0

我仍然不清楚什麼 - 如果兩個線程都調用async-append並傳入相同的文件名,那麼如何避免它們同時打開文件?看起來在async-append中調用file-agent會爲每個線程創建一個唯一的代理,對吧? – jhickner

+0

對不清楚的說明,我打算爲每個文件創建一個文件代理,然後通過異步追加或同步附加追加它,這取決於您是否需要異步或同步追加。你可以讓他們自己的代理人在地圖上找到正確的代理人。 –

1

我不認爲在Clojure中有特定的內置函數,但您可以使用標準的Java IO函數來執行此操作。這看起來像這樣:

(import '(java.io File RandomAccessFile)) 

(def f (File. "/tmp/lock.file")) 
(def channel (.getChannel (RandomAccessFile. f "rw"))) 
(def lock (.lock channel)) 
(.release lock) 
(.close channel) 
+0

我首先檢查了這條道路,但不幸的是,這個方法僅用於鎖定其他進程訪問文件。當在同一個虛擬機中使用多個線程時,如果同一虛擬機中的另一個線程已將文件鎖定,lock和tryLock都會引發異常,而不是在文件可​​用之前阻塞。 – jhickner

4

我會使用核心Clojure的功能locking這是用來如下:

(locking some-object 
    (do-whatever-you-like)) 

這裏some-object既可以是您要同步的文件本身,或者任意對象(如果你想要一個鎖來保護多個文件,這可能是有意義的)。

底層使用標準JVM對象鎖定,所以它基本上等同於Java中的同步代碼塊。