5

我正在使用 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 事件时,工作线程会收到信号以更改其正在执行的操作。

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

谢谢!

4

1 回答 1

5

第一:你真的需要一个后台线程来闲置吗?

在大多数平台上,启动一个新线程很便宜。(除了在 Windows 和 Linux 上,它非常便宜。)那么,为什么不在你需要的时候启动一个线程呢?(保持一个线程列表和单个线程一样容易,对吧?)

或者,为什么不创建一个ThreadPoolExecutor,然后向它提交作业,让执行者担心它们何时运行以及在哪个线程上运行。任何时候你只要考虑“需要在不阻塞主线程的情况下运行的任务”而不是“需要等待工作的工作线程”,你就会让你的生活更轻松。在幕后,仍然有一个或多个工作线程在队列上等待,或者类似的东西,但那部分都是为您编写的(并已调试和优化)。您只需要编写任务,它们只是常规功能。

但是,如果你想写显式的后台线程,你可以,所以我会解释。


如何在不占用 CPU 时间的情况下让工作线程空闲?... 向工作线程发出信号以执行某项操作的最佳方式是什么?

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

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

但是,通常 aQueue要简单得多。要等待 a Queue,只需使用 调用它的get方法block=True。要通知另一个线程唤醒,put只需Queue. (在幕后, aQueue包装了一个listordeque或其他集合, aLock和 a Condition,所以你只需告诉它你想要做什么——检查一个值,阻塞直到有一个值,添加一个值——而不是处理等待并发出信号并保护收藏。)

请参阅使用线程控制 wxPython 中的 UI 元素的答案,了解如何在两个方向上发出信号,从工作线程到 UI 线程,反之亦然。


我有一些方法让工作线程向 UI 本身注册回调,以便当单击按钮或发生任何其他 UI 事件时,工作线程会收到信号以更改其正在执行的操作。

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

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


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

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

如果您只想说“如果您空闲,请醒来并执行此任务;否则,请坚持并在您准备好时执行此任务”,这正是 aQueue或 anExecutor自动为您做的事情。

如果你想说,“如果你闲着,就醒来,否则,别担心”,这就是 a ConditionorEvent会做的事情。

如果您想说“如果您空闲,请醒来并执行此操作,否则,请取消您正在执行的操作并改为执行此操作”,这有点复杂。您几乎需要让后台线程在忙碌时定期检查“interrupt_me”变量(并在其Lock周围放置一个),然后您将设置该标志并通知Condition...尽管在某些情况下,您可以合并空闲和忙碌情况合并为一个ConditionEvent(空闲时调用无限,忙碌时wait()快速检查wait(timeout=0))。


* 在某些情况下——例如,LinuxfutexCriticalSectionWindows——在某些情况下,它实际上可能会占用一点 CPU 时间,因为这恰好是一个很好的优化。但关键是,在您准备好使用它之前,您不会要求任何 CPU 时间。

于 2013-05-09T23:34:44.003 回答