我有一个每五分钟在cron上运行一个进程。通常,运行只需几秒钟,但有时需要几分钟。我想确保一次只运行一个版本。
我尝试了一种显而易见的方式......
File.open("/tmp/indexer_lock.tmp",'w') do |f|
exit unless f.flock(File::LOCK_EX)
end
...但它没有测试是否可以获得锁定,它会在锁定被释放之前阻塞。
知道我错过了什么吗?我宁愿不使用ps来破解某些东西,但这是另一种选择。
答案 0 :(得分:23)
我知道这已经过时了,但是对于任何有兴趣的人来说,有一个非阻塞常量可以传递给flock,以便它返回而不是阻塞。
File.new("/tmp/foo.lock").flock( File::LOCK_NB | File::LOCK_EX )
flock
将返回true,否则返回false。因此,为了确保一次只运行一个进程,您只想尝试获取锁定,如果您无法执行,则退出。它就像在我上面的代码行前放exit unless
一样简单:
exit unless File.new("/tmp/foo.lock").flock( File::LOCK_NB | File::LOCK_EX )
答案 1 :(得分:4)
根据您的需要,这应该可以正常工作,不需要在任何地方创建另一个文件。
exit unless DATA.flock(File::LOCK_NB | File::LOCK_EX)
# your script here
__END__
DO NOT REMOVE: required for the DATA object above.
答案 2 :(得分:3)
您可以创建和删除临时文件并检查是否存在此文件。 请检查这个问题的答案: one instance shell script
答案 3 :(得分:3)
虽然这不是直接回答你的问题,但如果我是你,我可能会写一个守护程序脚本(你可以使用http://daemons.rubyforge.org/)
您可以让您的索引器(假设其indexer.rb)通过名为script / index的包装脚本运行,例如:
require 'rubygems'
require 'daemons'
Daemons.run('indexer.rb')
除了指定睡眠间隔
之外,你的索引器几乎可以做同样的事情loop do
# code executing your indexing
sleep INDEXING_INTERVAL
end
这就是与队列服务器串联的作业处理器通常如何运行。
答案 4 :(得分:3)
对于这种情况,有一个lockfile gem。我以前用过它而且很简单。
答案 5 :(得分:2)
如果您使用cron,可能更容易在cron调用的shell脚本中执行类似的操作:
#!/usr/local/bin/bash
#
if ps -C $PROGRAM_NAME &> /dev/null ; then
: #Program is already running.. appropriate action can be performed here (kill it?)
else
#Program is not running.. launch it.
$PROGRAM_NAME
fi
答案 6 :(得分:2)
这是一个单行代码,应该在任何Ruby脚本的顶部都可以使用:
exit unless File.new(__FILE__)).tap {|f| f.autoclose = false}.flock(File::LOCK_NB | File::LOCK_EX)
原始代码有两个问题。
首先,它被阻止的原因是#flock
的呼叫丢失了File::LOCK_NB
:
锁定时不阻塞。可以结合 与其他锁定选项一起使用逻辑或。
第二,如果一个File对象被关闭(无论是在上面代码中的#open
块的末尾,还是通过显式#close
或隐式自动关闭)当文件被垃圾回收时),基础文件描述符被关闭并且锁被释放。为防止这种情况,您可以设置#autoclose =
false
。
答案 7 :(得分:1)
好的,从@ shodanex的指针处理笔记,这就是我所拥有的。我稍微揉了一下(虽然我不知道Ruby中的触摸模拟)。
tmp_file = File.expand_path(File.dirname(__FILE__)) + "/indexer.lock"
if File.exists?(tmp_file)
puts "quitting"
exit
else
`touch #{tmp_file}`
end
.. do stuff ..
File.delete(tmp_file)
答案 8 :(得分:0)
你能否将File :: LOCK_NB添加到你的锁中,使其无阻塞(即如果它无法获得锁定则失败)
这适用于C,Perl等。
答案 9 :(得分:0)
在更高级别,您可能会发现lock_method宝石有用:
def the_method_my_cron_job_calls
# something really expensive
end
lock_method :the_method_my_cron_job_calls
它默认使用存储在本地文件系统上的锁文件(上面讨论的内容),但您也可以配置远程锁存储:
LockMethod.config.storage = Redis.new([...]) # a remote RedisToGo instance, perhaps?
也...
def the_method_my_cron_job_calls
# something really expensive
end
lock_method :the_method_my_cron_job_calls, (60*60) # automatically expire lock after an hour