优雅地终止Python线程
我正在尝试写一个Unix客户端程序,这个程序会监听一个套接字、标准输入,并从文件描述符中读取数据。我把这些任务分配给了不同的线程,并且它们通过同步队列和信号量成功地与“主”应用程序进行通信。问题是,当我想要关闭这些子线程时,它们都在等待输入,无法继续执行。此外,线程不能在里面注册信号处理器,因为在Python中,只有主线程可以这样做。
有没有什么建议?
3 个回答
看看这些回答:
Python SocketServer
如何退出一个多线程程序?
简单来说,不要在 recv()
上卡住。可以用 select()
来设置一个超时时间,这样就可以检查一下这个socket是否可以读取。同时,当 select()
超时后,检查一下是否有退出的标志。
另外,线程不能注册信号处理程序。
在C语言中,杀死线程的信号可能会带来很大的麻烦,尤其是当你在线程中分配内存时,因为当某个线程结束时,这部分内存不会被释放(因为它属于进程的堆)。C语言没有垃圾回收机制,所以如果指针超出了作用域,那就真的没了,内存依然被占用。所以在这方面要小心——在C语言中,只有在你打算杀掉所有线程并结束进程时,才可以这样做,这样内存才能归还给操作系统。例如,从线程池中添加和移除线程会导致内存泄漏。
问题是,当我想关闭这些子线程时,它们都在等待输入。
有趣的是,我最近也在和这个问题斗争。解决办法就是不要进行没有超时的阻塞调用。所以,理想情况下,你想要的是:
def threadfunc(running):
while running:
blockingcall(timeout=1)
这里的running是从控制线程传递过来的——我虽然没用过线程,但用过多进程,实际上你需要传递一个Event()
对象并检查is_set()
。不过你问的是设计模式,这就是基本思路。
然后,当你想让这个线程结束时,你运行:
running.clear()
mythread.join()
你的主线程应该允许客户端线程处理它的最后一次调用,然后返回,整个程序就能顺利结束。
如果你有一个没有超时的阻塞调用该怎么办?使用异步选项,如果需要的话可以让线程“睡一会儿”(也就是调用你能让线程暂停一段时间的方法,这样就不会一直在转圈)。没有其他办法了。
这个问题没有好的解决办法,特别是当线程在等待的时候。
我遇到过类似的问题(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属性来设置。