
该文章提到的 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) 1 ClericPy 2023-03-14 18:53:35 +08:00 孤儿进程吗? 杀了以后 wait 试一下 |
| /td> | 3 evemoo OP > 如果 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 。  |
4 evemoo OP 总算解决了,某种意义上算是给自己埋坑。 第一个点,可执行程序如果有-d 参数这种后台运行的参数,subprocess 调用的时候要去掉,不然会以独立进程存在。 第二个点,不用 communicate(),统一输出 log 到文件。communicate()加上 timeout 参数捕获异常就无法 kill 子进程,实在是诡异 |
5 yinmin 2023-03-24 18:24:36 +08:00 proc.terminate() 改成 proc.kill() 试试。linux 一定能杀掉,windows 不知道是否可行。 |