1

我需要能够将 control-c 发送到我启动的新线程。我该怎么办。我可以启动线程,但我需要像在命令行中使用 control-c 一样停止。

from Tkinter import *
import threading # should use the threading module instead!
import Queue
import os


def show1():
    os.system('ola_recorder -p MyRecord -i 0')

def show2 ():
    os.system('ola_recorder -p MyRecord ')

def stop ():
    os.system('^c')


t = threading.Thread(name='Show2', target=show2)

root = Tk()
b = Button(root, text="Show 1", command=lambda: thread.start_new(show1, ()))
b.pack()

b2 = Button(root, text="Show 2", command=lambda: t.start())
b2.pack()

root.mainloop()
4

1 回答 1

5

首先,您正在做的事情不起作用的原因是:

每次调用os.system只调用您平台的system(或者_system,在某些 Windows 平台上)函数,每次都会创建一个全新的 shell 进程。因此,您无法做任何事情os.system来影响另一个呼叫。


如果要将 ^C 发送到任意进程,可以使用os.kill. 要使其可移植,您必须执行以下操作:

def send_ctrl_c(pid):
    try:
        sig = signal.CTRL_C_EVENT
    except AttributeError:
        sig = signal.SIGINT
    else:
        os.signal(pid, sig)

但是,您需要其他进程的 pid 来执行此操作,并且os.system不给您。


所以,正确的做法是使用subprocess模块:

proc2 = None

def show2():
    global proc2
    proc2 = subprocess.Popen('ola_recorder -p MyRecord', shell=True)
    proc2.wait()

由于没有充分的理由在这里使用 shell,你可能最好传递Popen一个 args 列表,而不是一个字符串,并放弃shell=True. 当然,不坚持proc2全局会更干净,但对于这个玩具示例,我将忽略这一点。

无论如何,现在您可以获取 pid 并使用它:

def stop():
    send_ctrl_c(proc2.pid)

但是,如果您要这样做,您不妨Popen直接使用该对象。有关您可以使用它做什么的完整详细信息,请参阅文档,但这里有一个快速版本:

def stop():
    global proc2
    try:
        sig = signal.CTRL_C_EVENT
    except AttributeError:
        sig = signal.SIGINT
    proc.send_signal(sig)

当您调用 时stop,该进程将被终止,就像它收到一个 ^C (POSIX),或者尽可能接近它收到一个 ^C (Windows),wait调用将返回(带有 -2,至少在 POSIX 上),您的线程也将完成。


最后一点:您几乎不想thread直接使用模块,也不想重用threading.Thread对象。所以,而不是这个:

b = Button(root, text="Show 1", command=lambda: thread.start_new(show1, ()))

… 或这个:

b2 = Button(root, text="Show 2", command=lambda: t.start())

… 做这个:

def start2():
    global t2
    t2 = threading.Thread(name='Show2', target=show2)
    t2.start()

当然,一个非玩具程序应该避免使用全局,并且希望跟踪所有现有线程而不是只跟踪最后一个(如果允许您在后台线程已经运行时单击按钮),并且可能会希望join在退出时将其所有线程。

于 2013-06-17T22:59:30.090 回答