Python 子进程终止

5 投票
4 回答
807 浏览
提问于 2025-04-16 23:34

我遇到了一个问题,就是无法结束子进程。下面这段代码是用来创建子进程的:

  while(not myQueue.empty()): 
        p=Popen(myQueue.get(),shell=True,stdin=PIPE,stderr=PIPE)

我通过不断循环,直到队列(里面有命令)为空来创建进程。变量 p 是全局的,类型是 Popen。尽管命令已经完成了它应该做的事情,但我在停止按钮上遇到了问题,它没有像我预期的那样停止进程。

停止按钮的代码如下:

  stop=Button(textBoxFrame,text="Stop",width=5,command=stopAll)
  stop.grid(row=1,column=4)

上面的停止按钮会调用 stopAll 方法,这个方法会结束当前的子进程 p。

  def stopAll():
        p.kill()

注意:没有错误、异常或编译问题。

更新:问题在于 p.kill() 并没有结束我想要结束的进程。我用 unix 检查了这一点,使用了 >> ps aux。我还让我的程序输出启动和结束的进程 ID(PID),这样我可以用 ps aux 来检查它们。我发现我需要结束的进程距离 p.pid 有 6 个 PID 的差距,我尝试用 os.kill((p.pid)+6,signal.SIGKILL) 来结束那个进程,这样是有效的,能够停止正确的进程。但我不想这样做,因为这样可能会导致其他子进程被错误地结束。我会提供更多关于我问题的细节:

我这里使用的队列包含了命令,正如我之前提到的。命令大致是这样的:

    echo "Hello"|festival --tts

Festival 是一个在 unix 上的语音合成器,festival --tts 从文件中获取用户输入。我把 "Hello" 传给 festival,它能正确地说出这些话。但是,p 这个执行上述命令的进程却结束了 echo 而不是 festival。所以请帮我结束特定的(festival)进程。

4 个回答

0

正如@aid提到的,如果你不明确把这个句柄设置为全局的,你就无法为其他人更改它。试试这个,替代你的stopAll函数。

def stopAll():
    global stopPressed
    stopPressed=True

我建议与其去搞全局变量,不如创建一个类,比如说:

class GuiController(object):
    # if you want the stopPressed to be a static variable accross all
    # GuiController instances uncomment the next line and comment out the __init__
    #stopPressed = False
    def __init__(self):
        self.stopPressed=False
    def main(self):
        while(not myQueue.empty()): 
            p=Popen(myQueue.get(),shell=True,stdin=PIPE,stderr=PIPE)
            while(p.returncode==None):
                if(stopPressed==True):
                    p.kill()
                    break
        self.stopPressed=False
    def stopAll(self):
        self.stopPressed=True
1

我怀疑你可能遇到问题是因为你没有在stopAll()这个函数里把stopPressed声明为全局变量。比如说,

>>> fred = '20'
>>> def updateFred(age):
...     fred=age
>>> updateFred(40)
>>> fred
'20'
>>> def updateFred(age):
...     global fred
...     fred=age
>>> updateFred(40)
>>> fred
40

也许在stopAll()的开头加上'global stopPressed'会有帮助?

0

因为这个节日程序是在UNIX系统上运行的,所以我们可以使用preexec_fn这个功能。Festival程序会自己创建一些子进程,这让我们很难结束它们。因此,这里我们可以利用一个组ID,它可以识别所有Festival相关的进程,这样就能解决这个问题。我们可以用下面的代码来创建一个进程组-

   while(not myQueue.empty()): 
    p=Popen(myQueue.get(),shell=True, stdin=PIPE,preexec_fn=os.setsid)

stopAll现在有了以下代码,用于结束由全局变量p指向的一组子进程-

    os.killpg(os.getpgid(p.pid), signal.SIGKILL)

这段代码会结束所有被创建的子进程。

来源:处理子进程

撰写回答