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

标签 file-io concurrency common-lisp race-condition allegro-cl

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

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

最佳答案

操作系统级别

CLISP提供stream-lockwith-stream-lock,其接口(interface)fcntlLockFileEx 。这些将锁定打开的流和文件。

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

目录只是一个(特殊)文件,因此 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)))

关于file-io - 在 Common Lisp 中处理并发文件访问,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24765355/

相关文章:

mysql - mysql如何处理并发访问?

java - 为什么此代码会抛出 java ConcurrentModificationException?

multithreading - 生成比线程更多的任务

command-line - 在 Common Lisp 中获取命令行参数

c - 如何将大量 uint_8 转换为 C 中的 float 组?

C++连续读取文件

C++ 和二进制文件 - 新手问题

java - 文件路径有疑问

list - 如何删除双括号并仅保留 lisp 中的嵌套列表?

java - 从 Java 执行 lisp 函数