优雅地终止Python线程

16 投票
3 回答
30172 浏览
提问于 2025-04-16 19:37

我正在尝试写一个Unix客户端程序,这个程序会监听一个套接字、标准输入,并从文件描述符中读取数据。我把这些任务分配给了不同的线程,并且它们通过同步队列和信号量成功地与“主”应用程序进行通信。问题是,当我想要关闭这些子线程时,它们都在等待输入,无法继续执行。此外,线程不能在里面注册信号处理器,因为在Python中,只有主线程可以这样做。

有没有什么建议?

3 个回答

-1

看看这些回答:

Python SocketServer
如何退出一个多线程程序?

简单来说,不要在 recv() 上卡住。可以用 select() 来设置一个超时时间,这样就可以检查一下这个socket是否可以读取。同时,当 select() 超时后,检查一下是否有退出的标志。

0

另外,线程不能注册信号处理程序。

在C语言中,杀死线程的信号可能会带来很大的麻烦,尤其是当你在线程中分配内存时,因为当某个线程结束时,这部分内存不会被释放(因为它属于进程的堆)。C语言没有垃圾回收机制,所以如果指针超出了作用域,那就真的没了,内存依然被占用。所以在这方面要小心——在C语言中,只有在你打算杀掉所有线程并结束进程时,才可以这样做,这样内存才能归还给操作系统。例如,从线程池中添加和移除线程会导致内存泄漏。

问题是,当我想关闭这些子线程时,它们都在等待输入。

有趣的是,我最近也在和这个问题斗争。解决办法就是不要进行没有超时的阻塞调用。所以,理想情况下,你想要的是:

def threadfunc(running):

    while running:
        blockingcall(timeout=1)

这里的running是从控制线程传递过来的——我虽然没用过线程,但用过多进程,实际上你需要传递一个Event()对象并检查is_set()。不过你问的是设计模式,这就是基本思路。

然后,当你想让这个线程结束时,你运行:

running.clear()
mythread.join()

你的主线程应该允许客户端线程处理它的最后一次调用,然后返回,整个程序就能顺利结束。

如果你有一个没有超时的阻塞调用该怎么办?使用异步选项,如果需要的话可以让线程“睡一会儿”(也就是调用你能让线程暂停一段时间的方法,这样就不会一直在转圈)。没有其他办法了。

9

这个问题没有好的解决办法,特别是当线程在等待的时候。

我遇到过类似的问题(Python: 如何终止一个阻塞线程),我能停止线程的唯一方法就是关闭底层的连接。这样一来,正在等待的线程就会抛出一个异常,然后我就可以检查停止标志并关闭它。

示例代码:

class Example(object):
   def __init__(self):
       self.stop = threading.Event()
       self.connection = Connection()
       self.mythread = Thread(target=self.dowork)
       self.mythread.start()     
   def dowork(self):

        while(not self.stop.is_set()):
             try:
                  blockingcall()        
             except CommunicationException:
                  pass
   def terminate():
       self.stop.set()
       self.connection.close()
       self.mythread.join()

还有一点需要注意的是,通常阻塞操作会提供一个超时选项。如果你有这个选项,我建议你使用它。最后我想说的是,你可以把线程设置为守护线程。

根据pydoc的说明:

线程可以被标记为“守护线程”。这个标记的意义在于,当只剩下守护线程时,整个Python程序会退出。初始值是从创建线程那里继承的。这个标记可以通过daemon属性来设置。

撰写回答