如何使用file# flock对排他锁发出非阻塞请求?



如何请求非阻塞锁?

为什么当单独尝试锁定文件时,Ruby的File#flock不能像预期的那样工作?将文件锁定在块中并不是解决这个问题的正确方法,因为关键是要显示对持久锁的锁定行为。在块中使用file# flock会在块退出时释放锁,因此它不能正确地演示问题。

file# flock以各种方式失败,特别是在请求非阻塞锁时。以下是一些例子:

文件#flock

的错误示例
  • 使用多个排他锁时无限等待,因为#flock没有提供超时锁请求的方法。

    # First lock succeeds.
    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    # This never returns.
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_EX)
    
  • 在文件处于排他锁状态时请求非阻塞锁会导致无效参数异常。

    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_NB)
    # => Errno::EINVAL: Invalid argument - foo
    
  • 文档中说#flock"根据locking_constant(下表中值的逻辑值)锁定或解锁文件"。然而,逻辑或会根据平台引发Errno::EINVALErrno::EBADF

    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_NB || File::LOCK_EX)
    # => Errno::EINVAL: Invalid argument - foo
    

原生文件#flock首选解决方案

当无法获得排他锁时,可能会使用Timeout模块引发Timeout::Error,似乎file# flock应该能够本地解决这个问题。那么,如何在不阻塞的情况下请求独占锁呢?

使用具有排他锁的Timeout模块

可以使用Timeout模块设置#flock获取排他锁的持续时间。下面的示例将触发Timeout::Error: execution expired,然后可以以适合应用程序的任何方式对其进行抢救。当计时器到期时返回nil,允许测试#flock表达式是否为真。

require 'timeout'
f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
f1.flock(File::LOCK_EX)
# => 0
f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
Timeout::timeout(0.001) { f2.flock(File::LOCK_EX) } rescue nil
# => nil

对非阻塞锁尝试使用位或

文件#flock的文档说:

根据locking_constant(下表中值的逻辑或值)锁定或解锁文件。如果指定了File::LOCK_NB,则返回false,否则操作将被阻塞。

但是,该方法实际上需要一个Bitwise OR操作符,而不是parse中定义的逻辑OR关键字。通过tOROP解析器标记。因此,允许#flock在独占锁失败时返回false的正确参数实际上是File::LOCK_NB|File::LOCK_EX。例如:

f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
f1.flock(File::LOCK_EX|File::LOCK_NB)
# => 0
f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
f2.flock(File::LOCK_NB|File::LOCK_EX)
# => false
f1.close; f2.close
# => nil

这将在可用时始终生成排他锁;否则,它会立即返回一个假值,而不会引发或挽救异常。这显然是该模块的使用方式,但文档可以使用一些说明和额外的示例来使其更容易理解。

相关内容

  • 没有找到相关文章

最新更新