使用多处理时无法终止加载动画

Cannot kill a loading animation when using multiprocessing

我正在尝试使用 multiprocessing 到 运行 多个脚本。一开始,我启动了一个加载动画,但我无法终止它。下面是一个例子...

动画:foo.py

import sys
import time
import itertools

# Simple loading animation that runs infinitely.
for c in itertools.cycle(['|', '/', '-', '\']):
    sys.stdout.write('\r' + c)
    sys.stdout.flush()
    time.sleep(0.1)

有用的脚本:bar.py

from time import sleep
# Stand-in for a script that does something useful.
sleep(5)

尝试 运行 他们两个:

import multiprocessing
from multiprocessing import Process
import subprocess

pjt_dir = "/home/solebay/path/to/project" # Setup paths..
foo_path = pjt_dir + "/foo.py" # ..
bar_path = pjt_dir + "/bar.py" # ..

def run_script(path): # Simple function that..
    """Launches python scripts.""" # ..allows me to set a.. 
    subprocess.run(["python", path]) # ..script as a process.

foo_p = Process(target=run_script, args=(foo_path,)) # Define the processes..
bar_p = Process(target=run_script, args=(bar_path,)) # ..

foo_p.start() # start loading animation
bar_p.start() # start 'useful' script 

bar_p.join() # Wait for useful script to finish executing
foo_p.kill() # Kill loading animation

我没有收到任何错误消息,(my_venv) solebay@computer:~$ 出现在我的终端中,但加载动画仍然存在(剪掉我的名字和环境)。我怎样才能杀死它?

我曾经 运行 遇到过类似的情况,之前我无法使用 ctrl + c 终止程序。这个问题(或多或少)是通过使用守护进程 processes/threads (see multiprocessing doc) 解决的。为此,您只需更改

foo_p = Process(target=run_script, args=(foo_path,)) 

foo_p = Process(target=run_script, args=(foo_path,), daemon=True) 

对于您想创建的其他子进程也是如此。

话虽如此,我自己也不确定这是否是解决无法终止多处理程序的问题的正确方法,或者它只是碰巧有助于解决此问题的一些工件。我建议 thread 更多地讨论守护进程线程。但本质上,根据我的理解,守护线程将在其父进程终止时自动终止,无论它们是否完成。同时,如果线程不是守护线程,那么您需要以某种方式等到子进程完成才能完全终止程序。

您创建的进程过多。这两行:

foo_p = Process(target=run_script, args=(foo_path,)) # Define the processes..
bar_p = Process(target=run_script, args=(bar_path,)) # ..

创建两个新进程。让我们将它们全部设为“A”和“B”。每个进程由这个函数组成:

def run_script(path): # Simple function that..
    """Launches python scripts.""" # ..allows me to set a.. 
    subprocess.run(["python", path]) # ..script as a process.

然后创建另一个子进程。我们称这两个进程为“C”和“D”。您总共创建了 4 个额外的进程,而不仅仅是您需要的 2 个。实际上是进程“C”在终端上产生输出。这一行:

bar_p.join()

等待“B”终止,这意味着“D”已终止。但是这一行:

foo_p.kill() 

杀死进程“A”但孤儿进程“C”。所以终端的输出会一直持续下去。

这有据可查 - 请参阅 multiprocessing.terminate 的描述,其中说:

“请注意,该进程的后代进程不会终止——它们只会变成孤立的。”

以下程序按您的预期运行,在第一个进程完成后从第二个进程正常退出。 (我将“foo.py”重命名为 useless.py,将“bar.py”重命名为 useful.py,并做了一些小改动,以便我可以在我的电脑上 运行。)

import subprocess
import os

def run_script(name):
    s = os.path.join(r"c:\pyproj310\so", name)
    return subprocess.Popen(["py", s])

if __name__ == "__main__":
    useless_p = run_script("useless.py")
    useful_p = run_script("useful.py")
    
    useful_p.wait() # Wait for useful script to finish executing
    useless_p.kill() # Kill loading animation

您不能使用 subprocess.run() 启动新进程,因为该函数将阻塞主脚本,直到进程完成。所以我改用 Popen。此外,我将 运行ning 代码放在 if __name__ == "__main__" 下,这是一个很好的做法(在 Windows 上可能是必要的)。