【问题标题】:Handling concurrent file access in Common Lisp在 Common Lisp 中处理并发文件访问
【发布时间】:2014-07-16 03:33:45
【问题描述】:

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

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

【问题讨论】:

  • @JoshuaTaylor 完全不同的 Lisp 实例。
  • @JoshuaTaylor 用户正在运行的 lisp 程序将直接访问文件系统(虽然不是必需的)。
  • @JoshuaTaylor Windows
  • @JoshuaTaylor Allegro CL

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


【解决方案1】:

操作系统级别

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

您可以使用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)))

【讨论】:

  • 这有助于锁定整个目录吗? (我链接到了一些 Allegro 文档,它看起来也不会锁定整个目录。)
  • @JoshuaTaylor:在 Windows 上没有,在 unix 上可能是。另见编辑。
  • 这很好! OP 在 Windows 上,所以第一点可能没有太大帮助,但协作锁看起来可能会奏效。
  • @sds 那么“应用级”代码背后的逻辑是,当存在另一个具有特殊锁定后缀的文件(同名)时,文件被“锁定”?
  • @ElliotPenson:是的,差不多。这是协作锁定,即,应用程序可能会忽略这些锁定。
【解决方案2】:

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

locking

功能

包:excl.osi

参数: 流模式长度

对打开的流应用、测试或移除 Windows 锁定。见 _locking 的 Windows API 文档,了解有关如何操作的更多信息 使用这个库函数。

在 UNIX 上不支持此功能,它会发出错误信号 类osi-not-supported

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多