Skip to main content
 首页 » 编程设计

python之即使在 KeyboardInterrupt 之后,多线程代码仍会继续打印

2024年10月17日32wayfarer

我有一个非常简单的多线程 python 代码,其中有两个线程试图从队列中弹出和打印。我使用锁来确保互斥。一切正常,除了:

  1. 如果我导入 python 的内置 Queue,程序会在终端的 KeyboardInterrup 上退出
  2. 如果我定义自定义 class Queue(object)(在内部实现为 list),即使在 KeyboardInterrupt< 之后,线程也会继续打印到终端。

这是我的代码:https://ideone.com/ArTcwE (虽然你不能在ideone上测试KeyboardInterrupt)

PS:我已经经历了Close multi threaded application with KeyboardInterrupt已经。它没有解决我的问题。

更新 1:我理解(感谢@SuperSaiyan 的回答)为什么线程会在场景 # 2 中继续工作——主函数在 job_done 可以设置之前就死了为 True。因此,线程一直在等待信号到达。但奇怪的是,即使在场景 # 1 中,job_done 仍然是 False。线程以某种方式被杀死:

>>> execfile('threaded_queue.py') 
Starting Q1Starting Q2 
 
Q1 got 0 
Q2 got 1 
Q1 got 2 
Q1 got 3 
 
 
Traceback (most recent call last): 
  File "<pyshell#68>", line 1, in <module> 
    execfile('threaded_queue.py') 
  File "threaded_queue.py", line 54, in <module> 
    while not q.empty(): 
KeyboardInterrupt 
>>> job_done 
False 
>>> 

更新 2:将代码粘贴到此处以实现永久性:

from time import time, ctime, sleep 
from threading import Thread, Lock 
from Queue import Queue 
 
class MyQueue(object): 
    def __init__(self): 
        self.store = [] 
 
    def put(self, value): 
        self.store.append(value) 
 
    def get(self): 
        return self.store.pop(0) 
 
    def empty(self): 
        return not self.store 
 
 
class SyncQueue(Thread): 
    __lock = Lock() 
 
    def __init__(self, name, delay, queue): 
        Thread.__init__(self) 
        self.name = name 
        self.delay = delay 
        self.queue = queue 
 
    def run(self): 
        print "Starting %s" % self.name 
        while not self.queue.empty(): 
            with self.__lock: 
                print "%s got %s" % ( 
                        self.name, 
                        self.queue.get()) 
            sleep(self.delay) 
        while not job_done: 
            sleep(self.delay) 
        print "Exiting %s" % self.name 
 
if __name__ == "__main__": 
    job_done = False 
    #q = Queue(5) # Python's Queue 
    q = MyQueue() # Custom Queue 
    for i in xrange(5): 
        q.put(i) 
    q1 = SyncQueue("Q1", .5, q) 
    q2 = SyncQueue("Q2", 1, q) 
    q1.start() 
    q2.start() 
 
    # Wait for the job to be done 
    while not q.empty(): 
        pass 
    job_done = True 
    q1.join() 
    q2.join() 
    print "All done!" 

请您参考如下方法:

您的问题不是您的自定义 Queue 与 python 的 Queue。这完全是另一回事。此外,即使使用 python 的 Queue 实现,您也会看到相同的行为。

这是因为当您按下 ctrl+C 时您的主线程在它能够向其他两个线程发出退出信号之前终止(使用 job_done = True)。

您需要的是一种告诉其他两个线程退出的机制。下面是一个简单的机制——您可能需要更强大的东西,但您会明白的:

try: 
    while not job_done: 
        time.sleep(0.1)  #Trying using this instead of CPU intensive `pass`. 
except KeyboardInterrupt as e: 
    job_done = True