4

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

编辑:对于这个问题的不同解决方案的想法也将不胜感激。

4

2 回答 2

5

操作系统级

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

您可以使用FFI在其他 CL 实现中调用这些 OS 函数。

目录只是一个(特殊)文件,因此fcntl应该能够锁定它(但必须仔细考虑“写入目录”的含义)。

不过,Windows 世界要复杂得多。我认为不可能使用库函数锁定目录。

应用级

您可以自己实现协作锁定。这意味着只有使用您的库的应用程序才会遵守锁定,因此您将能够修复应用程序之外的可能问题。

例如(未经测试!):

(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)))
于 2014-07-15T18:37:22.550 回答
3

Allegro CL 提供了许多操作系统特定的锁定机制。查看附录 A.10 OSI 文件锁定函数。特别是,特定于 Windows 的解决方案看起来像是lock,但是在同一页上描述的lock-streamunlock-stream是平台无关的版本,可以提供更便携的代码。但是,这些看起来确实是特定于文件的,因为它们是基于文件已经打开的。我不知道是否有锁定整个目录的选项。

锁定

功能

包:excl.osi

参数: 流模式长度

在打开的流上应用、测试或删除 Windows 锁定。_locking有关如何使用此库函数的更多信息,请参阅 Windows API 文档。

在 UNIX 上不支持此函数,它会发出 class 错误信号osi-not-supported

于 2014-07-15T18:39:18.737 回答