【问题标题】:Gracefully Terminating Python Threads优雅地终止 Python 线程
【发布时间】:2011-06-15 14:45:26
【问题描述】:

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

有什么建议吗?

【问题讨论】:

  • 什么样的线程?有一些库提供线程功能。
  • 不幸的是,这只是使用单独 (POSIX) 线程进行连接时的冰山一角。考虑改用非阻塞 I/O(例如,使用 Twisted。)
  • 线程需要自行终止(或被告知这样做)。你能让它们不被阻塞,所以如果有信号它们就可以退出吗? (投票或其他)
  • 我正在使用线程模块,但在其他线程实用程序中发现了类似的困难。

标签: python multithreading


【解决方案1】:

没有解决此问题的好方法,尤其是当线程阻塞时。

我遇到了类似的问题 (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()

要注意的另一件事是,通常阻塞操作通常会提供超时。如果你有这个选项,我会考虑使用它。我的最后一条评论是,您始终可以将线程设置为恶魔,

来自 pydoc:

可以将线程标记为“守护线程”。这个标志的意义在于,当只剩下守护线程时,整个 Python 程序就退出了。初始值继承自创建线程。该标志可以通过 daemon 属性设置。

【讨论】:

  • 请注意,触摸正在被另一个线程使用的文件描述符是不安全的,它可能导致比另一个线程中的异常更糟糕的事情。您应该真正使用非阻塞 I/O。
  • +1 我 100% 同意,但有时你不能…… ;(
  • 不幸的是,虽然很少在文件描述符上进行操作。 (真正有问题的情况是阻塞根本无法中断操作的外部库:P)
【解决方案2】:

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

杀死线程的信号可能很可怕,尤其是在 C 语言中,尤其是当您将内存分配为线程的一部分时,因为当特定线程死亡时它不会被释放(因为它属于进程的堆)。 C 中没有垃圾收集,所以如果该指针超出范围,它就超出了范围,内存仍然被分配。因此,请小心那个 - 如果你要真正杀死所有线程并结束进程以便将内存交还给操作系统 - 只有在 C 中这样做 - 例如从线程池中添加和删除线程会给你一个内存泄漏。

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

有趣的是,我最近一直在与同样的事情作斗争。解决方案实际上是不要在没有超时的情况下进行阻塞调用。因此,例如,理想情况下您想要的是:

def threadfunc(running):

    while running:
        blockingcall(timeout=1)

运行是从控制线程传递的——我从未使用过线程,但我使用了多处理,因此您实际上需要传递一个Event() 对象并检查is_set()。但是你要求设计模式,这是基本思想。

然后,当你希望这个线程结束时,你运行:

running.clear()
mythread.join()

然后你的主线程应该允许你的客户端线程处理它的最后一次调用并返回,整个程序很好地折叠起来。

如果你有一个没有超时的阻塞调用,你会怎么做?如果需要,请使用异步选项和睡眠(如调用任何方法,您必须将线程暂停一段时间,这样您就不会旋转)。没有其他方法可以解决它。

【讨论】:

    【解决方案3】:

    查看这些答案:

    Python SocketServer
    How to exit a multithreaded program?

    基本上,不要阻塞recv(),使用select() 超时检查套接字的可读性,并在select() 超时时轮询退出标志。

    【讨论】:

    • 这是一个不错的解决方案,但我希望有一种更雄辩的方式来实现它,而不必不断循环。每当我真正想在等待 select 超时时终止线程时,这也会导致延迟。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 2015-06-07
    • 2012-03-16
    • 1970-01-01
    • 2019-11-10
    • 2011-01-04
    • 1970-01-01
    相关资源
    最近更新 更多