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

Python多进程运行两函数并终止其一遇AttributeError问题

多进程中全局变量无法共享导致terminate()调用报错

问题描述

我搭建了一套运行Blender渲染任务并测量GPU负载的环境,使用multiprocessing启动两个独立函数:一个运行Blender渲染,另一个用nvidia-smi监控GPU。我想在Blender渲染结束后终止GPU监控进程,于是把subprocess.Popen()返回的对象存在全局变量里,但执行时出现错误:

import os
import multiprocessing
import subprocess

nvidia_cmd = None

blender_cmd_to_run = '/home/mickey/dev/blender-4.2.1-linux-x64/blender -b /home/mickey/dev/elastic_shared_memory/benchmark_scenes/bmw27/bmw27.blend -f 10 -- --cycles-device CUDA'
nvidia_cmd_to_run = 'nvidia-smi --query-gpu=gpu_bus_id,memory.used --format=csv -l 1'

def run_blender():
    blender_cmd = subprocess.Popen(blender_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in blender_cmd.stdout:
        print(line.decode().strip())
    nvidia_cmd.terminate()

def run_nvidia():
    global nvidia_cmd
    nvidia_cmd = subprocess.Popen(nvidia_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in nvidia_cmd.stdout:
        print(line.decode().strip())


if __name__ == '__main__':
    blender_process = multiprocessing.Process(target=run_blender)
    nvidia_process = multiprocessing.Process(target=run_nvidia)

    nvidia_process.start()
    blender_process.start()

错误信息:

AttributeError: 'NoneType' object has no attribute 'terminate'

问题原因

Python的multiprocessing创建的子进程拥有独立的内存空间:

  • 主进程初始化nvidia_cmd = None后,每个子进程都会复制这个初始状态到自己的内存中。
  • run_nvidia函数在自己的子进程里修改了nvidia_cmd,但这个修改只存在于该子进程的内存中,run_blender所在的子进程里的nvidia_cmd仍然是初始的None。
  • 当Blender渲染结束后调用nvidia_cmd.terminate()时,自然会触发NoneType的属性错误。

解决方案

方案1:主进程统一管理终止逻辑(最简单)

让主进程等待Blender进程结束,然后主动终止GPU监控进程,无需跨进程共享变量:

import os
import multiprocessing
import subprocess

blender_cmd_to_run = '/home/mickey/dev/blender-4.2.1-linux-x64/blender -b /home/mickey/dev/elastic_shared_memory/benchmark_scenes/bmw27/bmw27.blend -f 10 -- --cycles-device CUDA'
nvidia_cmd_to_run = 'nvidia-smi --query-gpu=gpu_bus_id,memory.used --format=csv -l 1'

def run_blender():
    blender_cmd = subprocess.Popen(blender_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in blender_cmd.stdout:
        print(line.decode().strip())

def run_nvidia():
    nvidia_cmd = subprocess.Popen(nvidia_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in nvidia_cmd.stdout:
        print(line.decode().strip())

if __name__ == '__main__':
    blender_process = multiprocessing.Process(target=run_blender)
    nvidia_process = multiprocessing.Process(target=run_nvidia)

    nvidia_process.start()
    blender_process.start()
    
    # 等待Blender渲染完成
    blender_process.join()
    # 终止GPU监控进程
    nvidia_process.terminate()
    nvidia_process.join()

方案2:用multiprocessing.Manager共享进程PID

通过Manager创建跨进程共享的字典,存储GPU监控进程的PID,Blender进程结束后通过PID终止该进程:

import os
import multiprocessing
import subprocess

blender_cmd_to_run = '/home/mickey/dev/blender-4.2.1-linux-x64/blender -b /home/mickey/dev/elastic_shared_memory/benchmark_scenes/bmw27/bmw27.blend -f 10 -- --cycles-device CUDA'
nvidia_cmd_to_run = 'nvidia-smi --query-gpu=gpu_bus_id,memory.used --format=csv -l 1'

def run_blender(shared_dict):
    blender_cmd = subprocess.Popen(blender_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in blender_cmd.stdout:
        print(line.decode().strip())
    # 通过PID终止GPU监控进程
    if 'nvidia_pid' in shared_dict:
        os.kill(shared_dict['nvidia_pid'], 15)  # 发送SIGTERM信号

def run_nvidia(shared_dict):
    nvidia_cmd = subprocess.Popen(nvidia_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    shared_dict['nvidia_pid'] = nvidia_cmd.pid
    for line in nvidia_cmd.stdout:
        print(line.decode().strip())

if __name__ == '__main__':
    with multiprocessing.Manager() as manager:
        shared_dict = manager.dict()
        blender_process = multiprocessing.Process(target=run_blender, args=(shared_dict,))
        nvidia_process = multiprocessing.Process(target=run_nvidia, args=(shared_dict,))

        nvidia_process.start()
        blender_process.start()
        blender_process.join()
        nvidia_process.join()

方案3:用multiprocessing.Pipe传递终止信号

通过管道在两个子进程间传递终止指令,GPU监控进程监听管道信号,收到后自行终止:

import os
import multiprocessing
import subprocess
import select

blender_cmd_to_run = '/home/mickey/dev/blender-4.2.1-linux-x64/blender -b /home/mickey/dev/elastic_shared_memory/benchmark_scenes/bmw27/bmw27.blend -f 10 -- --cycles-device CUDA'
nvidia_cmd_to_run = 'nvidia-smi --query-gpu=gpu_bus_id,memory.used --format=csv -l 1'

def run_blender(conn):
    blender_cmd = subprocess.Popen(blender_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    for line in blender_cmd.stdout:
        print(line.decode().strip())
    # 发送终止信号
    conn.send('terminate')
    conn.close()

def run_nvidia(conn):
    nvidia_cmd = subprocess.Popen(nvidia_cmd_to_run, shell=True, stdout=subprocess.PIPE)
    while True:
        # 同时监听stdout输出和管道信号
        ready, _, _ = select.select([nvidia_cmd.stdout, conn], [], [], 0.1)
        if nvidia_cmd.stdout in ready:
            line = nvidia_cmd.stdout.readline()
            if not line:
                break
            print(line.decode().strip())
        if conn in ready:
            signal = conn.recv()
            if signal == 'terminate':
                nvidia_cmd.terminate()
                break

if __name__ == '__main__':
    parent_conn, child_conn = multiprocessing.Pipe()
    blender_process = multiprocessing.Process(target=run_blender, args=(parent_conn,))
    nvidia_process = multiprocessing.Process(target=run_nvidia, args=(child_conn,))

    nvidia_process.start()
    blender_process.start()
    
    blender_process.join()
    nvidia_process.join()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 09:10:59