在 Rails 当中,经常需要将某些任务作为定时任务执行,而对于系统的定时任务而言,到点就启动一个进程来处理,相互之间是独立的,这就有可能导致某一些进程同时操作某个资源,有可能导致发生出现竟态,而导致一些问题。

通常的一个思路是通过一些外部的标志来达到加锁的作用,比如说文件。

来看一段代码:

lock do
  # handle the limited resource
end

这里的 lock 就是一个加锁和解锁的过程。

lock 的一个简单实现是:

def lock
  lock_file = "resource.lock"
  return if File.exists? lock_file
  FileUtils.touch lock_file

  yield
ensure
  FileUtils.rm_rf lock_file
end

还有一种更强大更优雅的做法:

def lock(timeout = 10)
  File.open(lock_file, "w+") do |f|
    begin
      f.flock File::LOCK_EX
      Timeout::timeout(timeout) { yield }
    ensure
      f.flock File::LOCK_UN
    end
  end
end

注意

  • 其中 File::LOCK_EX 是一个排他锁,在持有这个锁期间,只能有一个进程操作该文件。File::UN 代表解锁。
  • 另外,这里还做了一个超时判断,来防止进程死锁。

enjoy!