仅在未运行时使用cron运行python脚本

19 投票
3 回答
7610 浏览
提问于 2025-04-16 18:25

我需要每分钟运行一个 Python 脚本(job.py)。这个脚本如果已经在运行,就不能再启动。它的执行时间可能从10秒到几个小时不等。

所以我在我的定时任务(crontab)里加了:

* * * * * root cd /home/lorenzo/cron && python -u job.py 1>> /var/log/job/log 2>> /var/log/job/err

为了避免在脚本已经运行时再次启动它,我使用了 flock()。

这是脚本(job.py):

import fcntl
import time
import sys

def doIncrediblyImportantThings ():
    for i in range (100):
        sys.stdout.write ('[%s] %d.\n' % (time.strftime ('%c'), i) )
        time.sleep (1)

if __name__ == '__main__':
    f = open ('lock', 'w')
    try: fcntl.lockf (f, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except:
        sys.stderr.write ('[%s] Script already running.\n' % time.strftime ('%c') )
        sys.exit (-1)
    doIncrediblyImportantThings ()

这种方法似乎有效。

我有没有遗漏什么?使用这种方法会遇到什么问题吗?

有没有更好的或更“正规”的方法来实现这个功能?

感谢任何建议。

3 个回答

2

我上周遇到了这个问题,虽然找到了一些不错的解决办法,但我决定做一个非常简单、干净的Python包,并把它上传到PyPI上。

安装方法是:pip install quicklock

使用起来非常简单:

[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> # Let's create a lock so that only one instance of a script will run
...
>>> singleton('hello world')
>>>
>>> # Let's try to do that again, this should fail
...
>>> singleton('hello world')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/nate/live/gallery/env/lib/python2.7/site-packages/quicklock/quicklock.py", line 47, in singleton
    raise RuntimeError('Resource <{}> is currently locked by <Process {}: "{}">'.format(resource, other_process.pid, other_process.name()))
RuntimeError: Resource <hello world> is currently locked by <Process 24801: "python">
>>>
>>> # But if we quit this process, we release the lock automatically
...
>>> ^D
[nate@Nates-MacBook-Pro-3 ~/live] python
Python 2.7.6 (default, Sep  9 2014, 15:04:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from quicklock import singleton
>>> singleton('hello world')
>>>
>>> # No exception was thrown, we own 'hello world'!

可以看看这个链接:https://pypi.python.org/pypi/quicklock

3

当机器重启或卡住时,如果脚本正在运行(这时候有一个活动的锁),你就会遇到麻烦。解决这个问题的简单方法是使用 @reboot 这个定时任务,让它在重启时运行 rm /path/to/lock,这样就可以删除那个锁了。

15

我唯一的建议是让你的异常处理更具体一些。你不想有一天不小心删掉了 fcntl 的导入,然后导致出现 NameError 这个错误而不知道。总是尽量捕捉你想处理的最具体的异常。在这种情况下,我建议你可以这样做:

import errno

try:
    fcntl.lock(...)
except IOError, e:
    if e.errno == errno.EAGAIN:
        sys.stderr.write(...)
        sys.exit(-1)
    raise

这样的话,任何导致锁无法获取的其他原因都会显示出来(可能会通过邮件通知你,因为你在用 cron),然后你可以决定这是不是需要管理员来处理的事情,或者是程序可以处理的其他情况,或者是其他什么。

撰写回答