在Python脚本中检查正在运行的Python脚本。

3
我正在运行一个Python脚本,可能需要几个小时才能完成。
在我的Python脚本开始时,我想检查这个Python脚本是否已经在运行。
如果它已经在运行,我想退出我刚刚启动的当前python。例如:
1AM开始运行Python并一直持续到3AM
在不知道已经有Python在运行的情况下,在2AM又启动了一个。我希望我的2AM Python能够检查并退出,因为它已经在运行了。
如何编写这个Python?
这是我尝试锁定的方法...
try:
    l = lock.lock("/home/auto.py", timeout=600) # wait at most 10 minutes

except error.LockHeld:
    e = sys.exc_info()[0]
    logging.error("Error: " + str(e) + " at main gatering Stats")
    smtpObj.sendmail(sender, receivers, message + "Error: " + str(e) + " at main gatering stats")
    exit("Fail: " + str(e) + " at main gathering Stats")
else:
    l.release()

所以我认为如果程序仍在运行,则等待10分钟后退出...如果程序已经停止运行,则运行当前的Python。


1
这与http://stackoverflow.com/questions/29354841/how-can-i-stop-my-python-script-when-another-python-script-is-running有何不同? - Padraic Cunningham
@PadraicCunningham 我尝试了大多数的答案,但要么不起作用,要么我无法让它起作用。 - Tim
@Tim请在此处发布您的代码。 - Saren Arterius
@Tim,如果正确创建了锁定文件,它应该可以工作,就像我删除的答案一样。你应该添加你尝试过什么以及它为什么不起作用。 - Padraic Cunningham
@PadraicCunningham 'mercurial/lock.py'? 'home/auto.py' 是我的Python路径。 - Tim
显示剩余14条评论
1个回答

2
你可以尝试使用带有r标志的lockfile-create命令,重试指定次数以捕获CalledProcessError并退出,-p标志将存储进程的pid
import os
import sys
from time import sleep

from subprocess import check_call, CalledProcessError

try:
    check_call(["lockfile-create", "-q","-p", "-r", "0", "-l", "my.lock"])
except CalledProcessError as e:
    print("{} is already running".format(sys.argv[0]))
    print(e.returncode)
    exit(1)


# main body

for i in range(10):
    sleep(2)
    print(1)

check_call(["rm","-f","my.lock"])

运行上述代码中的test.py脚本时,如果已经有一个脚本在运行,则会输出以下内容:
$ python  lock.py 
lock.py is already running
4

选项

-q, --quiet

抑制输出,仅通过退出状态表示成功或失败。

-v, --verbose

启用诊断输出。

-l, --lock-name

不要在文件名后添加.lock。此选项适用于lockfile-create、lockfile-remove、lockfile-touch或lockfile-check。

-p, --use-pid

每当创建锁定文件时,将当前进程ID(PID)写入锁定文件,并在检查锁定的有效性时使用该pid。有关更多信息,请参见lockfile_create(3)手册页。此选项适用于lockfile-create、lockfile-remove、lockfile-touch和lockfile-check。

-o, --oneshot

触摸锁并立即退出。此选项适用于lockfile-touch和mail-touchlock。如果未提供这些命令将永远运行,每分钟触摸一次锁定,直到被终止。

-r retry-count, --retry retry-count

尝试在放弃之前锁定filename retry-count 次。每次尝试都会延迟比上一次长一些时间(以5秒的增量),直到重试之间的最大延迟达到1分钟为止。如果未指定retry-count,则默认值为9,如果所有9个锁定尝试失败,则在180秒(3分钟)后放弃。

描述

lockfile_create函数以NFS安全的方式创建锁定文件。

如果flags设置为L_PID,则lockfile_create不仅会检查现有的锁定文件,而且还会读取其内容以查看是否包含ASCII进程ID。如果是,则仅当该进程仍然存在时,锁定文件才有效。

如果锁定文件在共享文件系统上,则可能是由远程主机上的进程创建的。因此,进程ID检查是无用的,不应设置L_PID标志。在这种情况下,没有好方法可以确定锁定文件是否过期。因此,如果锁定文件旧于5分钟,则将其删除。这就是为什么提供了lockfile_touch函数:在保持锁定的同时,需要通过调用lockfile_touch()定期刷新它(每隔一分钟左右)。

lockfile_check函数在不尝试创建新的锁定文件的情况下检查是否已存在有效的锁定文件。

最后,lockfile_remove函数删除锁定文件。

算法

用于以原子方式创建锁定文件(甚至在NFS上)的算法如下:

1

创建一个唯一的文件。在printf格式中,文件名为.lk%05d%x%s。第一个参数(%05d)是当前进程ID。第二个参数(%x)由time(2)返回值的4位副本组成。最后一个参数是系统主机名。

2

然后使用link(2)创建锁定文件。忽略link的返回值。

3

现在stat()锁定文件。如果stat失败,则跳转到步骤6。

4

将锁定文件的stat值与临时文件的stat值进行比较。如果它们相同,则获得了锁。删除临时文件并向调用者返回0(成功)。

5

检查现有的锁定文件是否有效。如果无效,则删除旧的锁定文件。

6

重试之前,我们暂停n秒。 n最初为5秒,但每次重试都会增加5秒,最多增加60秒(递增退避)。然后我们继续执行步骤2,直到达到重试次数。

似乎在Redhat上有一个称为lockfile-progs的等效软件包。lockfile-progs

在Mac上,您可以使用lockfile并执行以下操作:

import os
import sys
from time import sleep
import os
from subprocess import Popen, CalledProcessError, check_call


p = Popen(["lockfile", "-r", "0", "my.lock"])
p.wait()
if p.returncode == 0:
    with open("my.pid", "w") as f:
        f.write(str(os.getpid()))
else:
    try:
        with open("my.pid") as f:
            # see if process is still running or lockfile
            # is left over from previous run.
            r = f.read()
            check_call(["kill", "-0", "{}".format(r)])
    except CalledProcessError:
        # remove old lock file and create new
        check_call(["rm", "-f", "my.lock"])
        check_call(["lockfile", "-r", "0", "my.lock"])
        # update pid
        with open("my.pid", "w") as out:
            out.write(str(os.getpid()))
        print("Deleted stale lockfile.")
    else:
        print("{} is already running".format(sys.argv[0]))
        print(p.returncode)
        exit(1)
# main body

for i in range(10):
    sleep(1)
    print(1)
check_call(["rm", "-f", "my.lock"])

在您的情况下,也许使用套接字会起作用:
from socket import socket, gethostname, error, SO_REUSEADDR, SOL_SOCKET
from sys import argv
import  errno



sock = socket()

# Create a socket object
host = gethostname()  
# /proc/sys/net/ipv4/ip_local_port_range is  32768  61000 on my Ubuntu Machine
port = 60001  
# allow connection in TIME_WAIT
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)

try:
    sock.bind((host, port))
    sock.connect((host, port))
except error as e:
    # [Errno 99] Cannot assign requested address
    if e.errno == errno.EADDRNOTAVAIL:
        print("{} is already running".format(argv[0]))
        exit(1)
    # else raise the error
    else:
        raise e

# main body
from time import sleep

while True:
    print(1)
    sleep(2)

sock.close()

你能从bash中运行lockfile-create吗? - Padraic Cunningham
我认为它们是...但它没有安装在那里。 - Tim
Linux版本3.10.0-123.20.1.el7.x86_64 - Tim
cat /etc/redhat-release 输出了什么内容? - Padraic Cunningham
如果你使用的是Redhat而不是Ubuntu,那么请注意。脚本中存在一些问题,例如如果它崩溃了,锁定文件将不会被删除,所以我稍后或明天会解决这些问题。现在,请安装http://man.flashnux.com/en/redhat/9/9.0/man1/lockfile.1.html。 - Padraic Cunningham
显示剩余15条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接