我正在使用Python和wxPython编写应用程序。
我正在考虑实现这个目标的方法可能不是最好的 - 如果是这样的话,请告诉我,因为我愿意重构。
现在,我有一个GUI表单。主程序起始点实例化GUI表单的实例然后运行wx.mainLoop(),这会导致应用程序的主要初始线程在应用程序的生命周期内阻塞。
我们当然知道,当UI中发生事件时,UI线程会为它们运行代码。
现在,我有另一个线程 - 一个工作线程。该线程需要处于空闲状态,然后当UI线程中发生某些事情时,例如单击一个按钮,我希望工作线程停止空转并执行其他操作 - 运行一个函数,比方说。
我现在无法想象这一点,但我可以看到,当应用程序变得更加复杂时,还必须发出工作线程,而实际上正忙着做某事。
我对此设置有两个问题:
如何在不耗尽CPU时间的情况下使我的工作线程空闲?像while True: pass
那样做会吸引CPU时间,而像while True: time.sleep(0.1)
这样的东西不会对事件做出即时反应。
向工作线程发出信号的最佳方式是什么?我不希望UI线程执行某些操作,我希望UI线程发出工作线程信号,它应该改变它正在做的事情。理想情况下,我可以通过某种方式让工作线程向UI本身注册回调,这样当单击按钮或发生任何其他UI事件时,工作线程会发出信号以更改其正在执行的操作。
那么,这是实现这一目标的最佳方式吗?什么是最好的方法呢?
谢谢!
答案 0 :(得分:5)
首先:你真的需要一个后台线程来摆放闲置吗?
在大多数平台上,启动新线程很便宜。 (除了在Windows和Linux上,它是 supercheap 。)那么,为什么不在需要时立即启动线程? (将线程列表保存为单个线程同样容易,对吗?)
或者,为什么不创建一个ThreadPoolExecutor
,只需向其提交作业,让执行者担心它们何时运行以及在哪个线程上运行。任何时候你都可以考虑“需要在不阻塞主线程的情况下运行的任务”,而不是“需要等待工作的工作线程”,这样你就可以让生活更轻松。在封面下,仍然有一个或多个工作线程在队列中等待,或者等效的东西,但是这部分内容都已经为您编写(并进行了调试和优化)。所有你必须写的是任务,它们只是常规函数。
但是,如果你想写明确的后台线程,你可以,所以我会解释一下。
如何在不耗尽CPU时间的情况下使我的工作线程空闲? ...向工作线程发出信号的最佳方式是什么?
空闲线程直到值准备就绪的方法是等待同步对象。在任何现代操作系统上,等待同步对象意味着操作系统停止为您提供任何CPU时间,直到对象为您准备好。*
您可以在Threading
模块文档中看到各种不同的选项,但在大多数情况下使用的显而易见的选项是Condition
。然后,发出工作线程信号的方式是notify
Condition
。
然而,Queue
通常更简单。要等待Queue
,只需使用get
调用其block=True
方法即可。要通知另一个线程唤醒,只需put
Queue
上的某些内容。 (在Queue
封面下,list
或deque
或其他收藏,Lock
和Condition
,您只需告诉它你想做 - 检查一个值,阻止直到有值,添加一个值 - 而不是处理等待和信号并保护集合。)
请参阅controlling UI elements in wxPython using threading的答案,了解如何在两个方向发出信号,从工作线程到UI线程,反之亦然。
我可以通过某种方式让工作线程向UI本身注册一个回调,这样当点击一个按钮或任何其他UI事件发生时,工作线程会发出信号以改变它正在做的事情。
如果你愿意,你可以这样做。只需传递self.queue.put
或def callback(value): self.value = value; self.condition.notify()
或其他任何回调,GUI线程甚至不必知道回调正在触发另一个线程。
事实上,这是一个非常好的设计,当你决定在内联和后台线程之间来回移动一些代码,或者将它移到子进程而不是后台线程时,可能会让你非常高兴,或者其他什么。
我现在无法想象这一点,但我可以看到,当应用程序变得更加复杂时,还必须在工作线程实际忙于做某事时发出信号。
但如果它很忙,你想发生什么?
如果您只是想说“如果您处于空闲状态,请先唤醒并执行此任务;否则,请在准备就绪时抓住它并执行此操作”,这正是Queue
或Executor
,会自动为您完成。
如果你想说,“如果你闲着,请醒来,否则,不要担心”,这就是Condition
或Event
会做的事情。
如果你想说,“如果你是空闲的,请醒来并做到这一点,否则,取消你正在做的事情而不是这样做”,这有点复杂。您几乎需要让后台线程在忙碌时定期检查“interrupt_me”变量(并在其周围放置Lock
),然后您将设置该标志以及通知Condition
...虽然在某些情况下,您可以将空闲和忙碌案例合并为一个Condition
或Event
(通过在空闲时调用无限wait()
并快速检查{{1}忙的时候。)
*在某些情况下 - 例如,Linux wait(timeout=0)
或Windows futex
- 在某些情况下它实际上可能会占用一点CPU时间,因为这恰好是一个很好的优化。但问题是,在您准备好使用它之前,您不会询问任何CPU时间。