为什么Ruby的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)
在文件被独占锁定时请求非阻塞锁定会导致无效的参数异常。
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::EINVAL
或Errno::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
虽然在无法获得排他锁时可能会使用Timeout module来提升Timeout::Error
,但似乎File#flock应该能够原生解决此问题。那么,实际上如何在不阻塞的情况下请求独占锁?
答案 0 :(得分:17)
您可以使用Timeout module设置#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
File#flock的文档说:
根据locking_constant(逻辑或下表中的值)锁定或解锁文件。如果指定了File :: LOCK_NB,则返回false,否则操作将被阻止。
但是,该方法实际上需要一个Bitwise OR运算符,而不是tOROP解析器令牌的defined in 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
这将在可用时始终生成独占锁定;否则,它会立即返回一个虚假值,而不会产生提升或挽救异常的开销。这显然是该模块的使用方式,但文档可以使用一些说明和其他示例,以便更容易理解。