2011-06-15 111 views
12

我正在尝试编写一个正在侦听套接字,stdin和从文件描述符中读取的unix客户端程序。我将这些任务中的每一个分配给单独的线程,并使用同步队列和信号成功地与“主”应用程序通信。问题是,当我想关闭这些子线程时,它们都是输入阻塞的。此外,线程无法在线程中注册信号处理程序,因为在Python中只允许执行的主线程执行此操作。优雅地终止Python线程

有什么建议吗?

+0

什么样的线程?有几个库提供了线程功能。 – Blender 2011-06-15 14:47:25

+0

不幸的是,当使用单独的(POSIX)线程进行连接时,这只是冰山一角。请考虑使用非阻塞I/O(例如,使用Twisted)。 – 2011-06-15 14:53:19

+0

线程需要自行终止(或被告知这样做)。你可以让他们不被阻止,所以他们可以退出,如果信号? (polling或其他) – 2011-06-15 14:54:09

回答

7

没有好办法解决这个问题,特别是当线程阻塞时。

我有一个类似的问题(Python: How to terminate a blocking thread),我能阻止我的线程的唯一方法是关闭底层连接。这导致阻塞的线程升起和异常,然后让我检查停止标志并关闭。

示例代码:

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() 

另外一点需要注意的是常用阻塞操作一般提供了一个暂停。如果你有这个选择,我会考虑使用它。我最后的评论是,你总是可以设置线程deamonic,

从是pydoc:

线程可以被标记为“守护线程”。这个标志的意义在于,只有守护进程线程退出时,整个Python程序才会退出。初始值是从创建线程继承的。该标志可以通过守护进程属性设置。

+2

请注意,触摸另一个线程正在使用的filedescriptor是不安全的,它可能导致比其他线程中的异常更糟糕的事情。您应该真正使用非阻塞I/O。 – 2011-06-15 14:52:17

+0

+1我100%同意,但有些时候你不能......;( – Nix 2011-06-15 14:53:02

+0

不幸的是,虽然很少,当它是你正在操作的filedescriptors时(真正有问题的情况是阻止没有这是一个不错的解决方法:P) – 2011-06-15 14:54:54

0

见这些问题的答案:

Python SocketServer
How to exit a multithreaded program?

基本上,通过使用select()有超时检查套接字的可读性recv()不阻止,并查询一个退出标志时select()超时。

+0

这是一个不错的解决方案,但我希望有一个更有说服力的方法来实现这一点,而不必经常循环。这也会导致延迟,只要我真的想杀死线程while它等待选择超时。 – George 2011-06-15 15:15:05

+0

这就是线程的世界...... – theorifice 2016-10-25 12:05:19

1

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

信号杀线程是潜在的恐怖,特别是在C,尤其是如果分配存储器中作为螺纹的一部分,因为它不会被释放当特定的线程死亡时(因为它属于进程的堆)。 C中没有垃圾回收,所以如果该指针超出范围,它就超出了范围,内存仍然分配。所以只需要小心一点 - 只有在C中这样做才能杀死所有线程并结束进程,从而将内存交还给操作系统 - 例如,从线程池中添加和删除线程会给你一个内存泄漏。

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

有趣的是,最近我一直在用同样的东西打架。解决方法是从字面上不会在没有超时的情况下发出阻止呼叫。因此,例如,你想要什么理想的是:

def threadfunc(running): 

    while running: 
     blockingcall(timeout=1) 

其中运行从控制线程过去了 - 我从来没有用过线程,但我已经使用多以及与此你真正需要传递一个Event()对象,检查is_set()。但是你要求设计模式,这是基本的想法。

然后,当你想这个线程结束,我们可以运行:

running.clear() 
mythread.join() 

,然后你的主线程应该让你的客户的线程来处理它的最后一次通话,并返回,整个程序更好地折叠起来。

如果您在没有超时的情况下阻止呼叫,您会做什么?如果需要,可以使用异步选项,并进入休眠状态(如调用任何方法来暂停线程一段时间,以免不旋转)。没有别的办法。