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

多进程场景下进程意外终止后,如何避免恢复时的阻塞问题?

解决多进程恢复中join()阻塞的问题

核心问题原因

你遇到的阻塞问题本质是join()方法的特性:调用后会阻塞当前进程,直到目标进程执行完毕。在恢复函数中调用它,会导致恢复进程卡在等待新启动的业务进程结束,无法继续监控其他进程的状态。

正确实现思路

  1. 移除恢复函数中的join():重启业务进程后,不需要等待它完成,恢复函数应立即回到监控循环,继续检查其他进程状态。
  2. 使用进程间共享的PID列表:原代码中直接传递普通列表给子进程,修改的只是副本,其他进程无法获取更新后的PID。需要用multiprocessing.Manager创建可共享的列表。
  3. 保持监控循环的独立性:恢复进程的核心是持续轮询所有目标进程状态,无需绑定到单个重启进程的生命周期。

修改后的完整代码

import multiprocessing as mp
import time
import random
import psutil
import os

def proc_1(timepass=1, repeat=20):
    repeated = True
    i = 0
    while repeated:
        x = random.uniform(0, timepass)
        i += 1
        if i < repeat:
            print(f"Proc_1: {i}, will fall asleep for: {x}")
        else:
            repeated = False
            print("Proc_1 COMPLETED!")
            break
        time.sleep(x)

def proc_2(timepass=1, repeat=20):
    repeated = True
    i = 0
    while repeated:
        x = random.uniform(0, timepass)
        i += 1
        if i < repeat:
            print(f"Proc_2: {i}, will fall asleep for: {x}")
        else:
            repeated = False
            print("Proc_2 COMPLETED!")
            break
        time.sleep(x)

def process_killer(PID_list, timepass, exclusion_list=[]):
    while True:
        x = random.uniform(0, timepass)
        time.sleep(x)
        this_process_pid = os.getpid()
        parent_process = psutil.Process(os.getppid())
        child_pid_list = [p.pid for p in parent_process.children(recursive=True) if p.pid != this_process_pid]
        child_pid_list = list(set(child_pid_list) - set(exclusion_list))
        
        print('\n'.join([f'Process_{i + 1} PID: {child_pid_list[i]}' for i in range(len(child_pid_list))]))

        if len(child_pid_list) > 0:
            number = random.randint(0, len(child_pid_list)-1) if len(child_pid_list)>1 else 0
            try:
                kill_proc = psutil.Process(child_pid_list[number])
                if kill_proc.name() == "python.exe" and psutil.pid_exists(kill_proc.pid):
                    print(f"We kill the process with PID {kill_proc.pid}")
                    kill_proc.kill()
                    print(f"Process with PID {kill_proc.pid} killed.")
            except psutil.NoSuchProcess:
                print(f"Process with PID {child_pid_list[number]} not found.")

def process_recovery(process_pid_list):
    while True:
        for i in range(len(process_pid_list)):
            if not psutil.pid_exists(process_pid_list[i]):
                print(f'Process with PID {process_pid_list[i]} is dead')
                print(f'Restoring the process')
                # 根据索引重启对应业务进程
                if i == 0:
                    process = mp.Process(target=proc_1, kwargs={'timepass': 2, 'repeat': 30})
                else:
                    process = mp.Process(target=proc_2, kwargs={'timepass': 3, 'repeat': 30})
                process.start()
                # 更新共享列表中的PID
                process_pid_list[i] = process.pid
                temp_str = '\n'.join([f'Process_{idx + 1} PID: {process_pid_list[idx]}' for idx in range(len(process_pid_list))])
                print('Recovery result:\n' + temp_str)
                # 移除join(),避免阻塞
                # process.join()
        time.sleep(0.2)

if __name__ == "__main__":
    # 使用Manager创建进程间共享的列表
    with mp.Manager() as manager:
        PID_list = manager.list()
        
        # 启动初始业务进程
        process_1 = mp.Process(target=proc_1, kwargs={'timepass': 2, 'repeat': 30})
        process_2 = mp.Process(target=proc_2, kwargs={'timepass': 3, 'repeat': 30})
        process_1.start()
        process_2.start()
        
        PID_list.append(process_1.pid)
        PID_list.append(process_2.pid)
        
        # 启动恢复进程
        process_recov = mp.Process(target=process_recovery, kwargs={'process_pid_list': PID_list})
        process_recov.start()
        
        # 启动杀手进程,排除恢复进程PID
        process_kill = mp.Process(target=process_killer,
                                  kwargs={'PID_list': PID_list, 'timepass': 10, 'exclusion_list': [process_recov.pid]})
        process_kill.start()
        
        print("Main PID:", os.getpid())
        print("Process_1 PID:", process_1.pid)
        print("Process_2 PID:", process_2.pid)
        print("Process_killer PID:", process_kill.pid)
        print("Process_recovery PID:", process_recov.pid)
        
        # 主进程等待所有子进程(可选,根据需求调整)
        process_1.join()
        process_2.join()
        process_kill.join()
        process_recov.join()
        
        time.sleep(5)
        print("Program completed")

关键修改说明

  1. 移除process.join():恢复进程启动新业务进程后,立即回到监控循环,不会被单个进程的执行周期阻塞。
  2. 使用manager.list():解决了普通列表在进程间无法共享更新的问题,确保杀手进程和恢复进程都能获取最新的PID。
  3. 简化进程PID获取逻辑:优化了process_killer中获取子进程列表的代码,更简洁高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 18:58:08