You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保同一脚本(如Foo.pyx)同一时间仅运行一个实例?

确保Foo.pyx同一时间仅运行一个实例的几种方案

这是个非常常见的运维需求——不管是终止旧实例让新的运行,还是直接阻止新实例启动,核心都是避免多个实例同时占用资源。下面给你几个实用的实现思路,你可以根据自己的操作系统和具体需求选择:

方案1:用PID文件终止旧实例

这个思路是让脚本启动时先检查一个存放进程ID(PID)的文件(比如foo.pid):

  • 如果文件存在,读取里面的PID,检查对应进程是否还在运行
  • 如果进程存活,就杀掉它,然后更新PID文件为当前进程的PID
  • 如果进程已经不存在(比如之前脚本意外崩溃),就直接覆盖PID文件

代码示例(Linux/macOS)

在Foo.pyx的最开头加入这段代码:

import os
import signal

PID_FILE = "foo.pid"

def main():
    # 检查PID文件
    if os.path.exists(PID_FILE):
        with open(PID_FILE, 'r') as f:
            old_pid = int(f.read().strip())
        # 检查旧进程是否存在
        try:
            os.kill(old_pid, 0)  # 发送0信号不终止进程,仅检查是否存在
            print(f"发现旧实例(PID: {old_pid}),正在终止...")
            os.kill(old_pid, signal.SIGTERM)
            # 等待几秒确保进程退出,可选
            import time
            time.sleep(2)
            # 再检查一次,没退出就强制杀
            try:
                os.kill(old_pid, 0)
                os.kill(old_pid, signal.SIGKILL)
            except OSError:
                pass
        except OSError:
            # 旧进程不存在,删除无效PID文件
            os.remove(PID_FILE)
    # 写入当前PID到文件
    with open(PID_FILE, 'w') as f:
        f.write(str(os.getpid()))
    
    # 这里写你原来的脚本逻辑
    print("脚本开始运行...")
    # 模拟长时间运行
    import time
    time.sleep(30)

if __name__ == "__main__":
    try:
        main()
    finally:
        # 脚本退出时删除PID文件(可选,避免意外残留)
        if os.path.exists(PID_FILE):
            try:
                with open(PID_FILE, 'r') as f:
                    if int(f.read().strip()) == os.getpid():
                        os.remove(PID_FILE)
            except:
                pass

注意:Windows系统下os.kill的行为略有不同,需要用signal.CTRL_C_EVENT或者第三方库终止进程,后面的方案3会更适合跨平台场景。

方案2:用文件锁阻止新实例启动

如果你的需求是让旧实例继续运行,新实例直接退出,那么用系统级文件锁更可靠——因为PID文件可能因为脚本崩溃残留,而文件锁会在进程退出时自动释放。

跨平台代码示例

import os
import sys

LOCK_FILE = "foo.lock"

def acquire_lock():
    try:
        # 打开文件,模式为读写+创建,不截断
        fd = os.open(LOCK_FILE, os.O_RDWR | os.O_CREAT | os.O_EXCL)
        os.close(fd)
        return True
    except OSError:
        # 文件已存在,说明已有实例在运行
        return False

def main():
    if not acquire_lock():
        print("已有实例在运行,新实例退出...")
        sys.exit(1)
    
    try:
        # 原脚本逻辑
        print("脚本开始运行...")
        import time
        time.sleep(30)
    finally:
        # 退出时删除锁文件
        if os.path.exists(LOCK_FILE):
            os.remove(LOCK_FILE)

if __name__ == "__main__":
    main()

进阶优化(Linux/macOS):可以用fcntl的独占锁,不需要删除文件,进程退出自动释放:

import fcntl
import os
import sys

LOCK_FILE = "foo.lock"

def main():
    fd = open(LOCK_FILE, 'w')
    try:
        fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)  # 非阻塞独占锁
    except BlockingIOError:
        print("已有实例在运行,新实例退出...")
        sys.exit(1)
    
    # 原脚本逻辑
    print("脚本开始运行...")
    import time
    time.sleep(30)

if __name__ == "__main__":
    main()

方案3:用psutil检查进程(更可靠的跨平台方案)

上面的PID文件方案有个小问题:如果旧进程退出后,系统分配了相同的PID给其他进程,会误杀无辜。用psutil可以准确检查PID对应的进程是不是你的脚本。

步骤:

  1. 先安装psutil:pip install psutil
  2. 在脚本中加入检查逻辑

代码示例(跨平台,终止旧实例)

import os
import signal
import sys
import psutil

PID_FILE = "foo.pid"
SCRIPT_NAME = "Foo.pyx"

def is_my_script_running(pid):
    try:
        proc = psutil.Process(pid)
        # 检查进程的命令行是否包含脚本名称
        return SCRIPT_NAME in ' '.join(proc.cmdline())
    except (psutil.NoSuchProcess, psutil.AccessDenied):
        return False

def main():
    if os.path.exists(PID_FILE):
        with open(PID_FILE, 'r') as f:
            old_pid = int(f.read().strip())
        if is_my_script_running(old_pid):
            print(f"发现旧实例(PID: {old_pid}),正在终止...")
            try:
                proc = psutil.Process(old_pid)
                proc.terminate()
                proc.wait(timeout=3)
            except (psutil.NoSuchProcess, psutil.TimeoutExpired):
                proc.kill()
        else:
            # PID被复用,删除无效文件
            os.remove(PID_FILE)
    
    # 写入当前PID
    with open(PID_FILE, 'w') as f:
        f.write(str(os.getpid()))
    
    try:
        # 原脚本逻辑
        print("脚本开始运行...")
        import time
        time.sleep(30)
    finally:
        # 清理PID文件
        if os.path.exists(PID_FILE):
            try:
                with open(PID_FILE, 'r') as f:
                    if int(f.read().strip()) == os.getpid():
                        os.remove(PID_FILE)
            except:
                pass

if __name__ == "__main__":
    main()

这个方案可以准确识别自己的脚本进程,避免误杀,而且跨Windows、Linux、macOS都能用。


内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:09:22