python - 确保脚本只激活一次

时间:2013-11-03 10:58:08

标签: python python-2.7 atomicity

我正在写一个Python 2.7脚本 总之,这个脚本每晚都在Linux上运行,并激活多个进程。

我想确保此脚本不会并行运行多次(基本上是尝试模仿Singleton模式但在应用程序级别上运行。)

代码示例

def main():
    # before doing anything, I'd like to know whether this
    # script was activated and alive. 
    # if so, error out

    # do something

if __name__ == "__main__":
    main()

建议

天真的解决方案是创建一种锁定文件,它充当互斥锁。
我们要做的第一件事就是检查这个文件是否存在。如果是这样,那么脚本的其他实例已经创建了它,我们应该出错。脚本完成后,我们删除此文件 我假设这个解决方案可行,只要文件系统上的操作是原子的。

实施

import os, sys

lock_file_path = ".lock_script"

def lock_mutex():
    if os.path.exists(lock_mutex_path):
        print "Error: script was already activated."
        sys.exit(-1)

    else:
        file = open(lock_mutex_path, 'w')

def unlock_mutex():
    assert( os.path.exists(lock_mutex_path))
    os.remove(lock_mutex_path)

def main():

    try:
        lock_mutex()

        # do something

        unlock_mutex()

    except:
        unlock_mutex()

if __name__ == "__main__":
    main()

问题

如何确保lock_mutex()unlock_mutex()是原子的?

2 个答案:

答案 0 :(得分:4)

由于您使用的是Linux,因此可以使用flock

import os
import fcntl
import time

def main():
  # acquire the prog lock
  if not prog_lock_acq('singleton.lock'):
    print("another instance is running")
    exit(1)

  print("program is running-press Ctrl+C to stop")
  while True:
    time.sleep(10)

def prog_lock_acq(lpath):
  fd = None
  try:
    fd = os.open(lpath, os.O_CREAT)
    fcntl.flock(fd, fcntl.LOCK_NB | fcntl.LOCK_EX)
    return True
  except (OSError, IOError):
    if fd: os.close(fd)
    return False

if __name__ == '__main__':
  main()

退出prog_lock_acq后我们将文件保持打开状态并不重要,因为当进程退出时,操作系统会自动关闭它。此外,如果省略LOCK_NB选项,flock调用将会阻止,直到当前正在运行的进程退出。根据您的使用情况,这可能很有用。

请注意,我们不会在退出时删除该文件。没关系。文件的存在并不表示锁定的实时进程。因此,即使您使用kill -9终止了您的流程,锁仍然会被释放。

但是有一点需要注意:如果在进程运行时解除锁定文件的链接,那么当运行该进程的下一个实例时,它将创建一个没有锁定的新文件并且运行得很好将违反我们的单身人士设计。您可以使用目录做一些聪明的事情以防止取消链接,但我不确定它会有多强大。

答案 1 :(得分:1)

我使用supervisor(http://supervisord.org/)在Linux下运行东西。它运行Django,Celeryd等,并确保它们在意外完成时重新启动。

但也可以设置选项,以便命令在完成时不会自动启动或重新启动:autostart = false,autorestart = false,starseconds = 0。我将它用于这些cron工作。

在cron中我输入了命令“supervisorctl start myscript”,如果myscript已经在主管下运行,它什么都不做,否则启动它。

无论编写脚本的语言如何,都可以正常工作。