在Common Lisp中处理并发文件访问



多个用户需要使用Common Lisp中创建的接口访问相同的文件目录。当这种情况发生时,会出现许多竞态条件。例如,当多个用户同时添加或删除一个文件时。在lisp中是否有一种方法可以在工作完成时"锁定"特定的目录?这将是一个类似于多线程环境中的"同步"块的概念,但是我有单独的Lisp实例。我在Windows上使用Allegro CL。

编辑:对于这个问题的不同解决方案的想法也将受到赞赏。

操作系统级别

CLISP提供与fcntlLockFileEx接口的stream-lockwith-stream-lock。这将锁定打开的流和文件。

您可以使用FFI来调用其他CL实现中的操作系统函数。

目录仅仅是一个(特殊的)文件,所以fcntl应该能够锁定它(但是必须仔细考虑"写入目录"意味着什么)。

Windows的世界要复杂得多。我认为用库函数来锁定目录是不可能的。

App-level

您可以自己实现协作锁定。这意味着只有使用你的库的应用程序才会尊重锁,这样你就可以在应用程序之外修复可能的问题。

。(考验!):

(defun file-lock (f)
  "return the name of the lock file for this file"
  (concatenate 'sting f "-my-lock-suffix")) ; or use pathname functions...
(defun lock-file-once (f)
  "try to lock file once"
  (open (file-lock f) :direction :probe :if-exists nil))
(defun lock-file (f)
  "block until the file is locked"
  (loop :until (lock-file-once f)
    :do (sleep 1)))
(defun unlock-file (f)
  "remove the lock"
  (delete-file (file-lock f)))
(defmacro with-lock-file (f &body body)
  "lock the file, run body, unlock it"
  (let ((fn (gensym "with-lock-file-f")))
    `(let ((,fn ,f))
       (unwind-protect
            (progn (lock-file ,fn)
                   ,@body)
         (unlock-file ,fn)))))

锁定整个目录需要非常巧妙的方法来避免死锁:锁定一个目录意味着锁定它的所有子目录,因此获取一个文件的锁需要首先锁定该文件上面的所有内容,然后锁定该文件,然后解锁上面的所有内容。这打开了一个竞态条件。

简单的解决方案是有一个主锁,这是任何锁定操作所必需的:

(defvar *master-lock* (pathname .....))
(defun lock-file-or-directory-once (path)
  "lock file or directory or fail"
  (with-lock-file *master-lock*
    scan everything below and also above(!) path
    return nil if any relevant locks are found,
    i.e., if anything below path is locked
    or any directory above path is locked))
(defun lock-file-or-directory (path)
  "block until success"
  (loop :until (lock-file-or-directory path)
    :do (sleep 1)))

Allegro CL提供了许多特定于操作系统的锁定机制。参见附录a .10 OSI文件锁定函数。特别地,特定于windows的解决方案看起来像是锁定,但是锁流解锁流,在同一页中描述,是平台无关的版本,可以使代码更具可移植性。但是,这些看起来确实是特定于文件的,因为它们是基于已经打开的文件。我不知道是否有锁定整个目录的选项。

包:excl.osi

参数:流模式长度

在打开的流上应用、测试或删除Windows锁。看到_locking的Windows API文档了解更多信息

在UNIX上不支持这个函数,它表示错误class osi-not-supported .

最新更新