evemoo
V2EX  ›  Python

windows 下多线程执行 subporcess.Popen(),但子进程无法杀死

By evemoo at 2023 年 3 月 14 日 · 2234 次点击

该文章提到的 shell=False 不起作用。

简单说明:
一个可执行文件读取配置文件(非常多)在两秒内返回结果,即便修改 _work_queue 还是会因为无法关闭相应子进程导致内存溢出。

import shlex
from concurrent.futures import ThreadPoolExecutor

class BoundedThreadPoolExecutor(ThreadPoolExecutor):

    def __init__(self, max_workers, max_waiting_tasks, *args, **kwargs):
        super().__init__(max_workers=max_workers, *args, **kwargs)
        self._work_queue = Queue(maxsize=max_waiting_tasks)


def func(conf_path):
    try:
        command = f"../some.exe -d -c {conf_path}"
        args = shlex.split(command)
        proc = subprocess.Popen(args=args, shell=False, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        stdout, stderr = proc.communicate(5)
    except subprocess.TimeoutExpired:
        logger.error(f"pid:{proc.pid} communicate timeout")
    finally:
        proc.terminate()
    return proc


def callback2kill(res):
    proc = res.result()
    os.system(f"taskkill /F /pid {proc.pid}")
    

with BoundedThreadPoolExecutor(100, 500) as executor:
    for endpoint in endpoints:
        future = executor.submit(func, endpoint)
        future.add_done_callback(callback2kill)
6 条回复  •  2023-03-24 18:34:32 +08:00
ClericPy
   1
ClericPy  
   2023 年 3 月 14 日
孤儿进程吗? 杀了以后 wait 试一下
evemoo
   2
evemoo  
OP
   2023 年 3 月 14 日
@ClericPy
多线程里面 wait 会死锁,官方文档推荐 communicate()方法
evemoo
   3
evemoo  
OP
   2023 年 3 月 14 日
> 如果 shell=True ,那么会通过 shell 来启动进程。这意味着,一次 Popen 会启动两个进程,一个 shell 进程,一个命令进程。然后 Popen 返回的 pid 是 shell 进程的 pid ,这会导致 Popen.kill() 等函数不起作用,进程还在正常运行,所以一定要使用参数列表的形式启动,不要通过命令行的形式,不要使用 shell=True 。

但是还是解决不了子进程问题
简单复现:
run.bat 写入 ping -n 10 127.0.0.1
启动一个线程执行 subprocess.Popen(args="run.bat"),communicate(timeout=1),捕获 subprocess.TimeoutExpired 。
然后发现依然是要等 run.bat 程序 ping 完 10 次才会退出程序,但纯命令行执行 subprocess.Popen(args="ping -n 10 127.0.0.1")能捕获到并提前 kill 。
![image]( https://user-images.githubusercontent.com/42791572/224993848-874948bb-2836-4995-90e0-4faccb4ea861.png)
evemoo
   4
evemoo  
OP
   2023 年 3 月 14 日
总算解决了,某种意义上算是给自己埋坑。
第一个点,可执行程序如果有-d 参数这种后台运行的参数,subprocess 调用的时候要去掉,不然会以独立进程存在。
第二个点,不用 communicate(),统一输出 log 到文件。communicate()加上 timeout 参数捕获异常就无法 kill 子进程,实在是诡异
yinmin
   5
yinmin  
   2023 年 3 月 24 日
proc.terminate() 改成 proc.kill() 试试。linux 一定能杀掉,windows 不知道是否可行。
evemoo
   6
evemoo  
OP
   2023 年 3 月 24 日
@yinmin 是的,树莓派下 proc.kill() 无任何问题,windows 就是杀不掉
推荐学习书目
› Learn Python the Hard Way
Python Sites
› PyPI - Python Package Index
› http://diveintopython.org/toc/index.html
› Pocoo
值得关注的项目
› PyPy
› Celery
› Jinja2
› Read the Docs
› gevent
› pyenv
› virtualenv
› Stackless Python
› Beautiful Soup
› 结巴中文分词
› Green Unicorn
› Sentry
› Shovel
› Pyflakes
› pytest
Python 编程
› pep8 Checker
Styles
› PEP 8
› Google Python Style Guide
› Code Style from The Hitchhiker's Guide
© 2026 V2EX · 25ms · 3.9.8.5