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

12

如何请求非阻塞锁?

当单独尝试锁定文件时,为什么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)
  • 当文件被独占锁定时请求非阻塞锁会导致无效参数异常。

  • 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 "根据锁定常量(表格下方值的逻辑或)锁定或解锁文件"。但是,逻辑或会在不同平台上引发 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应该能够以本地方式解决此问题。那么,如何请求独占锁而不会阻塞呢?

    1个回答

    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。

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

    当可用时,这将始终生成一个独占锁;否则,它会立即返回一个虚值,而不会引发或拯救异常的开销。显然,这是模块预期的使用方式,但文档可能需要一些澄清和附加示例,以便更容易理解。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接