Python多线程脚本未正常启动/工作

0 投票
2 回答
14116 浏览
提问于 2025-04-20 03:39

我快速写了这个示例脚本给stackoverflow,所以功能方面的细节可以忽略(我自己写的版本看起来好多了),但是:

from threading import Thread
from msvcrt import getch       #I'm using Windows by the way.
from time import sleep
import sys

def KeyEvent():
    while True:
        key = getch()
        if key.encode('hex') == '03': #^C
            y = raw_input("Are you sure you want to quit? (y/n): ").lower()
            if y == 'y':
                sys.exit(0)
            else: pass

def main():
    t = 0
    while True:
        print "The count is {0}".format(t)
        t +=1
        sleep(1)

if __name__ == "__main__":
    mainthread = Thread(target = main)
    kev = Thread(target = KeyEvent)

    mainthread.daemon = True
    kev.daemon = True

    mainthread.start()
    kev.start()

这个脚本的目的是同时运行两个循环,一个是每秒计数,另一个是检查是否按下了^C(也就是中断命令)。请不要建议我用其他方法,因为这只是一个示例。

我遇到的问题是,这个脚本根本不启动。它显示“计数是0”,然后就退出了。但是如果我完全去掉.daemon = True的部分,脚本就能运行,但它没有正确执行sys.exit(0)。我该如何让这个脚本正确运行,并在需要时退出呢?

2 个回答

0

你遇到了两个问题。第一个是你的程序结束得太早。第二个是你试图在 KeyEvent 线程中退出程序。

你的程序之所以早早结束,是因为你没有让主线程保持运行。你的主线程在执行完最后一行代码 kev.start() 后就结束了,而你的守护线程也随之停止了。

你需要一种方法让主线程一直保持运行(因为你希望用户输入 "y" 来退出程序)。有几种方法可以做到这一点。一个方法是在代码的最后加上

mainthread.join()

第二,sys.exit(0) 在你的 KeyEvent 线程中显然无法终止整个程序。你可以在下面的帖子中找到答案:为什么在 Python 中在线程内调用 sys.exit() 不会退出?

3

被标记为 daemon 的线程会在其他所有非守护线程结束时自动停止。在你的情况中,主线程在调用了两个守护线程的 start() 之后就结束了,这也导致了整个 Python 进程的结束。你的线程能否执行任何操作,完全是运气和时机的问题。

sys.exit(0) 只会结束主线程,而不会结束其他线程。你需要一种方法来通知你的线程该停止了。实现这一点的一种方法是使用 事件 对象。

不要使用 getch 来捕捉 Ctrl+C,建议使用信号处理器来实现:

from threading import Thread
from threading import Event
from time import sleep
import signal

stop = Event()

def handler(signum, frame):
    y = raw_input("Are you sure you want to quit? (y/n): ").lower()
    if y == 'y':
        stop.set()

def main():
    t = 0
    while not stop.isSet():
        print "The count is {0}".format(t)
        t +=1
        sleep(1)

if __name__ == "__main__":
    signal.signal(signal.SIGINT, handler)

    mainthread = Thread(target = main)
    mainthread.start()

    while mainthread.is_alive():
        try:
            mainthread.join(timeout = 0.1)
        except IOError:
            pass #Gets thrown when we interrupt the join

撰写回答