使用File#flock作为ruby全局锁(进程互斥)

fcy6dtqo  于 2023-10-18  发布在  Ruby
关注(0)|答案(3)|浏览(124)

我有两个进程之间的并发问题,经过短暂的研究,我发现temporary file是这个问题的建议解决方案。
因此,解决方案是创建/tmp/global.lock并将其用作全局锁。我在这个线程Mutex for Rails Processes中找到了这个例子
到目前为止对我来说是有意义的,但我希望看到这个解决方案的最佳实践。上面解释了有意义,但我想知道如何检查给定的文件是否被锁定?

fh = File.open("/some/file/path", File::CREAT)

begin
  if locked = check_file_locked?
    sleep(1)
  else
    fh.flock(File::LOCK_EX)
    # do what you need to do
  end
ensure
  fh.flock(File::LOCK_UN)
end

这是我对解决方案的理解,不知道如何实现提到的check_file_locked?()?如果有最好的方法,我也很想听听。

mzsu5hc0

mzsu5hc01#

@bjhaid的回答可能会导致一个问题,那就是在Rubinius中,#timeout会导致一个解释器错误。这也是不必要的复杂。
这里有一个更简单的版本,使用非阻塞锁而不是超时:

def locked? lockfile_name
  f = File.open(lockfile_name, File::CREAT)

  # returns false if already locked, 0 if not
  ret = f.flock(File::LOCK_EX|File::LOCK_NB)

  # unlocks if possible, for cleanup; this is a noop if lock not acquired
  f.flock(File::LOCK_UN) 

  f.close
  !ret # ret == false means we *couldn't* get a lock, i.e. it was locked
end
sdnqo3pr

sdnqo3pr2#

当你对一个文件有一个排他锁时,试图在ruby中再次锁定它将无限期地等待,直到文件被解锁,所以你可以依靠它,并设置一个超时多长时间ruby应该等待,这可能不是最合适的方法,但我会这样做:

fh = File.open("/some/file/path", File::CREAT)
fh.flock(File::LOCK_EX)

require 'timeout'
def check_file_locked?(file)
  f = File.open(file, File::CREAT)
  Timeout::timeout(0.001) { f.flock(File::LOCK_EX) }
  f.flock(File::LOCK_UN)
  false
rescue 
  true
ensure
  f.close
end
f = File.open("/tmp/a.txt", "w+")
f.flock(File::LOCK_EX)
check_file_locked?("/tmp/a.txt") # => true
f.flock(File::LOCK_UN)
check_file_locked?("/tmp/a.txt") # => false
ebdffaop

ebdffaop3#

我为此创建了一个简单的Mutex类

class CrossProcessMutex
  def initialize(lock_filepath)
    @lock_filepath = lock_filepath
  end

  def synchronize
    # https://ruby-doc.org/3.2.0/File.html#method-i-flock
    f = File.open(@lock_filepath, File::RDWR|File::CREAT, 0644)
    f.flock File::LOCK_EX
    yield
  ensure
    f.flock File::LOCK_UN
  end
end

相关问题