如何使用File#flock对独占锁进行非阻塞请求?

我该如何申请非阻塞锁?

为什么在单独尝试锁定文件时,Ruby的File#flock不能按预期工作? 锁定块中的文件不是解决此问题的正确方法,因为重点是显示锁定持久锁的行为。 在块中使用File#flock会在块退出时释放锁定,因此它不能正确显示问题。

File#flock以各种方式失败,尤其是在请求非阻塞锁定时。 一些例子如下。

File#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) 
  • 在文件被独占锁定时请求非阻塞锁定会导致无效的参数exception。

     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(逻辑或下表中的值)锁定或解锁文件。” 但是,Logical OR会根据平台引发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模块设置#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 

使用按位OR进行非阻塞锁定尝试

File#flock的文档说:

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

但是,该方法实际上需要一个Bitwise OR运算符,而不是tOROP解析器令牌在parse.y中定义的逻辑OR关键字。 因此,允许#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 

这将在可用时始终生成独占锁定; 否则,它会立即返回一个虚假值,而不会产生提升或抢救exception的开销。 这显然是模块的使用方式,但文档可以使用一些说明和其他示例来使其更容易理解。

Interesting Posts