clojure中的惯用文件锁定?

clojure中的惯用文件锁定?,clojure,Clojure,我有一组队列中的未来处理作业,这些作业涉及到写入文件。确保一次只有一个人访问特定文件的惯用方法是什么?我认为Clojure中没有专门的内置函数,但您可以使用标准java IO函数来实现这一点。这看起来像这样: (import '(java.io File RandomAccessFile)) (def f (File. "/tmp/lock.file")) (def channel (.getChannel (RandomAccessFile. f "rw"))) (def lock (.l

我有一组队列中的未来处理作业,这些作业涉及到写入文件。确保一次只有一个人访问特定文件的惯用方法是什么?

我认为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)
(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)))

如何使用代理而不是锁来确保这一点

我认为在clojure中,使用代理来保护共享的可变状态(无论它是在内存中还是在磁盘上)比使用锁更为惯用

如果一次创建一个代理,并将访问尝试发送给代理,则可以确保每次仅在线程上访问给定文件

例如:

(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)
(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"))
如果需要同步使用该文件,可以使用wait实现。像这样:

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

我将使用核心Clojure函数,如下所示:

(locking some-object
  (do-whatever-you-like))
这里的
某些对象可以是文件本身,也可以是您想要同步的任意对象(如果您想要一个锁来保护多个文件,这可能是有意义的)


在后台,这使用标准的JVM对象锁定,因此它基本上相当于Java中的同步代码块。

我首先检查了这条途径,但不幸的是,此方法仅适用于锁定其他进程访问的文件。在同一个VM中使用多个线程时,如果同一VM中的另一个线程已经锁定了该文件,lock和tryLock都将引发异常,而不是在该文件可用之前阻塞。整洁!我花了一点时间才明白这一点。我最终使用了一个映射的引用,该映射包含每个打开文件的条目。您的解决方案看起来可能更好。我仍然不清楚一些事情-如果两个线程都调用async append并传入相同的文件名,那么如何防止它们都打开文件?在async append中调用文件代理似乎会为每个线程创建一个唯一的代理,对吗?很抱歉,说明不清楚,我的意思是为每个文件创建一个文件代理,然后通过async append或sync append将其追加,具体取决于您需要异步或同步追加的时间。你可以把这些代理保存在地图中,也许可以找到正确的代理。当然,如果你只需要同步访问,最好用类似的方式使用ref或atom和手表。这次使用的可变状态实体可能会保留文件的全部内容,而不是补丁信息。如果您需要根据当前内容进行更改,这可能非常有用。如果您需要更好的基于函数的文件更改和异步访问,那么当然也可以对代理执行同样的操作。如果您得到指向同一文件的两个不同文件对象(或两个不同的字符串对象),您如何确保实际锁定文件?有多种选择。如果您不关心繁重的并发工作负载,那么使用单例就可以了。如果您希望并行访问多个文件,请使用interning或哈希映射来确保所使用的每个文件都具有唯一的字符串名称。或者首先确保不要创建指向同一文件的重复对象。感谢您的建议-我最近使用了String/intern。但我的观点是,你的例子可能有误导性,因为问题使用了
文件名
,在这种情况下,它不会正常工作;也许可以更新你的答案?谢谢