设置空闲线程/信令线程

时间:2013-05-09 23:09:42

标签: python multithreading wxpython

我正在使用Python和wxPython编写应用程序。

我正在考虑实现这个目标的方法可能不是最好的 - 如果是这样的话,请告诉我,因为我愿意重构。

现在,我有一个GUI表单。主程序起始点实例化GUI表单的实例然后运行wx.mainLoop(),这会导致应用程序的主要初始线程在应用程序的生命周期内阻塞。

我们当然知道,当UI中发生事件时,UI线程会为它们运行代码。

现在,我有另一个线程 - 一个工作线程。该线程需要处于空闲状态,然后当UI线程中发生某些事情时,例如单击一个按钮,我希望工作线程停止空转并执行其他操作 - 运行一个函数,比方说。

我现在无法想象这一点,但我可以看到,当应用程序变得更加复杂时,还必须发出工作线程,而实际上正忙着做某事。

我对此设置有两个问题:

  1. 如何在不耗尽CPU时间的情况下使我的工作线程空闲?像while True: pass那样做会吸引CPU时间,而像while True: time.sleep(0.1)这样的东西不会对事件做出即时反应。

  2. 向工作线程发出信号的最佳方式是什么?我不希望UI线程执行某些操作,我希望UI线程发出工作线程信号,它应该改变它正在做的事情。理想情况下,我可以通过某种方式让工作线程向UI本身注册回调,这样当单击按钮或发生任何其他UI事件时,工作线程会发出信号以更改其正在执行的操作。

  3. 那么,这是实现这一目标的最佳方式吗?什么是最好的方法呢?

    谢谢!

1 个答案:

答案 0 :(得分:5)

首先:你真的需要一个后台线程来摆放闲置吗?

在大多数平台上,启动新线程很便宜。 (除了在Windows和Linux上,它是 supercheap 。)那么,为什么不在需要时立即启动线程? (将线程列表保存为单个线程同样容易,对吗?)

或者,为什么不创建一个ThreadPoolExecutor,只需向其提交作业,让执行者担心它们何时运行以及在哪个线程上运行。任何时候你都可以考虑“需要在不阻塞主线程的情况下运行的任务”,而不是“需要等待工作的工作线程”,这样你就可以让生活更轻松。在封面下,仍然有一个或多个工作线程在队列中等待,或者等效的东西,但是这部分内容都已经为您编写(并进行了调试和优化)。所有你必须写的是任务,它们只是常规函数。

但是,如果你想写明确的后台线程,你可以,所以我会解释一下。


  

如何在不耗尽CPU时间的情况下使我的工作线程空闲? ...向工作线程发出信号的最佳方式是什么?

空闲线程直到值准备就绪的方法是等待同步对象。在任何现代操作系统上,等待同步对象意味着操作系统停止为您提供任何CPU时间,直到对象为您准备好。*

您可以在Threading模块文档中看到各种不同的选项,但在大多数情况下使用的显而易见的选项是Condition。然后,发出工作线程信号的方式是notify Condition

然而,Queue通常更简单。要等待Queue,只需使用get调用其block=True方法即可。要通知另一个线程唤醒,只需put Queue上的某些内容。 (在Queue封面下,listdeque或其他收藏,LockCondition,您只需告诉它你想做 - 检查一个值,阻止直到有值,添加一个值 - 而不是处理等待和信号并保护集合。)

请参阅controlling UI elements in wxPython using threading的答案,了解如何在两个方向发出信号,从工作线程到UI线程,反之亦然。


  

我可以通过某种方式让工作线程向UI本身注册一个回调,这样当点击一个按钮或任何其他UI事件发生时,工作线程会发出信号以改变它正在做的事情。

如果你愿意,你可以这样做。只需传递self.queue.putdef callback(value): self.value = value; self.condition.notify()或其他任何回调,GUI线程甚至不必知道回调正在触发另一个线程。

事实上,这是一个非常好的设计,当你决定在内联和后台线程之间来回移动一些代码,或者将它移到子进程而不是后台线程时,可能会让你非常高兴,或者其他什么。


  

我现在无法想象这一点,但我可以看到,当应用程序变得更加复杂时,还必须在工作线程实际忙于做某事时发出信号。

但如果它很忙,你想发生什么?

如果您只是想说“如果您处于空闲状态,请先唤醒并执行此任务;否则,请在准备就绪时抓住它并执行此操作”,这正是QueueExecutor,会自动为您完成。

如果你想说,“如果你闲着,请醒来,否则,不要担心”,这就是ConditionEvent会做的事情。

如果你想说,“如果你是空闲的,请醒来并做到这一点,否则,取消你正在做的事情而不是这样做”,这有点复杂。您几乎需要让后台线程在忙碌时定期检查“interrupt_me”变量(并在其周围放置Lock),然后您将设置该标志以及通知Condition ...虽然在某些情况下,您可以将空闲和忙碌案例合并为一个ConditionEvent(通过在空闲时调用无限wait()并快速检查{{1}忙的时候。)


*在某些情况下 - 例如,Linux wait(timeout=0)或Windows futex - 在某些情况下它实际上可能会占用一点CPU时间,因为这恰好是一个很好的优化。但问题是,在您准备好使用它之前,您不会询问任何CPU时间。