【问题标题】:How to make only one instance of the same script running at any time [duplicate]如何使同一脚本的一个实例在任何时候都运行[重复]
【发布时间】:2018-09-12 05:40:04
【问题描述】:

假设我们有一个很长的脚本:Foo.pyx
如果我执行两次,它们将同时运行。

如果我执行两次,如何终止第一个?

编辑:终止后面的也可以,我只想随时运行一个实例。

【问题讨论】:

  • 您是否有理由要终止第一个而不是任何后续的?
  • @PeterGibson 也可以
  • @zwer 是的,解决方案从那里开始工作,谢谢

标签: python windows python-3.x terminate


【解决方案1】:

对此的传统 POSIX 答案是使用 pidfile:一个存储在一致位置的文件,该文件仅保存进程的 PID。

您想要存储 PID 的原因是,如果脚本被终止,您的程序的新副本将能够看到并接管,而不是错误地报告另一个副本已经在运行。 (可以用os.kill发送0信号,成功则表示进程存在。)

有一些库可以为您执行此操作并获得所有详细信息,但一个简单的版本是:

import os
import sys

PIDFILE = '/var/run/myscript.pid'

def is_running():
    try:
        with open(PIDFILE) as f:
            pid = int(next(f))
        return os.kill(pid, 0)
    except Exception:
        return False

if __name__ == '__main__':
    if is_running():
        print('Another copy is already running')
        sys.exit(0)
    with open(PIDFILE, 'w') as f:
        f.write(f'{os.getpid()}\n')

您可能希望在实际代码中进行更清晰的错误处理,并且您需要考虑竞争,但我保持简单以使想法清晰。 (如果你想真正做到这一点,你可能最好使用一个库,或者自己考虑所有问题,而不是从 SO 上复制代码。)


传统的 Windows 答案有点不同。在 Windows 上,很容易在退出时自动释放的文件或其他资源上获得强制排他锁。事实上,当您打开文件时,默认情况下会发生这种情况——但不幸的是,Python 的open 不会发生这种情况,它不遗余力地允许共享。您可以使用 PyWin32 中的 win32api 之类的库,或者只是 ctypes,调用 CreateFile 请求独占访问。如果您因为文件被锁定而失败,则有另一个进程正在运行。

决定在哪里创建文件也很重要。通常您使用每个用户的临时目录而不是全局目录,这意味着每个用户可以运行一份程序副本,而不是整个系统的一份副本。

【讨论】:

  • 非常感谢,记住这些
  • Windows Python 以读写共享方式打开文件,所以open 在这里没有任何帮助。您必须通过 PyWin32 或 ctypes 调用 CreateFile。鉴于使用了用户的临时目录,这允许每个用户一个实例。或者,可能需要每个 Windows 会话只允许一个实例。为此,Python 的 mmap 模块支持每个会话的名称,例如m = mmap.mmap(-1, 4, tagname='python_foo'). 如果为零,我们会尝试将 pid 写入共享内存。否则退出。写完后,把pid读回来,如果不是我们的,也退出。
  • @eryksun 哎呀,你是对的。我的旧代码使用 win32api 调用 CreateFile。我会编辑答案。
【解决方案2】:

脚本启动时,检查特定文件是否存在。 myscript.lock 是一个不错的名称选择,但任何名称都可以。

如果存在,脚本应立即退出。否则,创建一个该名称的空文件并继续执行脚本。然后在脚本完成后删除文件。

【讨论】:

  • 这其实是个很聪明的办法
  • 可以使用独占模式 ("x") 解决竞争条件,但除非它在 ​​Windows 上,或者我认为更新版本的 Linux 上,否则您无法可靠地删除文件,如果脚本崩溃或突然终止。如果是在 Windows 上,您可以使用fd = os.open(filename, os.O_CREAT | os.O_EXCL | os.O_TEMPORARY)。这将以关闭时删除模式打开文件,即使进程终止,也保证会被删除。
猜你喜欢
  • 2013-12-08
  • 1970-01-01
  • 2015-10-31
  • 2014-11-28
  • 2017-08-16
  • 2018-11-27
  • 1970-01-01
  • 2011-09-22
  • 1970-01-01
相关资源
最近更新 更多