【问题标题】:wxPython non-blocking GUI threading AND multiprocessing?wxPython 非阻塞 GUI 线程和多处理?
【发布时间】:2012-06-11 17:25:34
【问题描述】:

Python 2.7.3 x64 wxPython 2.8 x64

阅读了大量有关 python 线程和多处理的文章,尤其是 Doug Hellmann 的一些文章,这些文章帮助很大。但是,我对一件事感到困惑......

认为 Python 多处理模块或多或少是线程模块的替代品,除了 args 必须是可腌制的,但我发现这是为了不阻塞我的 GUI,我必须首先使用 threading.Thread 创建一个新线程,然后使用 multiprocessing.Process 在该线程中创建多进程。这行得通,而且效果很好,但对我来说似乎有点笨拙。

如果我尝试在没有第一个线程的情况下直接进行多处理,那么我的 GUI 仍然会阻塞,直到多处理作业完成。这是否按设计工作,还是我缺少有关多处理模块的一些基本内容?

如果需要示例,我可以提供。

谢谢,

-RMWChaos

要求提供示例...

假设 onProcess() 是由 GUI 中的按钮触发的,这会阻塞 GUI...

import time
import multiprocessing as mp

def myWorker(a, b):
    time.sleep(0.1)
    print '{} * {} = {}'.format(a, b, a*b)

def onProcess(event):
    jobs = mp.cpu_count() * 2
    a = 5
    b = 10

    for job in range(jobs):
        mp.Process(target = myWorker, args = (a, b,)).start()

虽然这不是...

import time
import multiprocessing as mp
import threading as th

def myWorker(a, b):
    time.sleep(0.1)
    print '{} * {} = {}'.format(a, b, a*b)

def onProcess(event):
    a = 5
    b = 10
    th.Thread(target = myThread, args = [a, b,]).start()

def myThread(a, b):
    jobs = mp.cpu_count() * 2

    for job in range(jobs):
        mp.Process(target = myWorker, args = (a, b,)).start()

【问题讨论】:

  • 也许你应该添加一个小例子来描述你的问题。

标签: python multithreading wxpython multiprocessing nonblocking


【解决方案1】:

多线程和多处理是根本不同的。

线程大部分用于 i/o。当您创建一个新线程时,它包含在与您生成该线程的程序相同的进程中。这意味着它与程序共享内存空间,但它们(程序和线程)不能并行运行(还要查找 GIL)。

另一方面,多处理会在操作系统级别产生一个新进程。这个新进程可以与预先存在的进程并行运行,但它不与生成它的程序共享内存空间。当您要加速的代码与 i/o 无关,而是实际的处理器密集型计算时,这会更有用。

对于 gui,您通常希望对 gui 的不同部分使用线程,以便在 gui 的一个部分中运行某些东西不会锁定整个 gui,直到处理结束。

没有代码很难说,但我相信你不应该特别需要在新线程中产生一个进程,而是让那个线程处理处理。但是,如果该线程需要处理密集型计算类型的处理而不是大量 i/o,那么您可能需要启动一个进程来处理它。

我相信您的大部分问题在于对多处理与多线程的误解。

【讨论】:

  • 我认为这回答了这个问题。基本上,我希望计算密集型和 i/o 密集型进程在后台运行而不会阻塞 GUI。这告诉我,我需要同时进行 - 线程化以便 GUI 不被阻塞,以及多处理以将工作负载分散到多个内核。
  • 那么您正在寻找的是多线程。生成一个新线程来处理事件,这应该可以解决阻塞 gui 其余部分的问题。此外,您可能希望生成一个进程来处理线程内部的处理,但这更多的是设计和特定问题。
  • 和我上面提供的第二个例子差不多?
  • 确实如此。当涉及到你在生成的线程和与 gui 相关的进程中所做的事情时要小心。请务必查看一些示例,了解如何确保您的 gui 线程更新安全。我不确定 wxPython 是如何工作的,但通常如果你尝试从主 gui 线程以外的任何地方更新 gui,你会遇到麻烦。
  • 好提示。所以我想这意味着使用类似 Queue() 的东西来放置衍生线程和进程的结果,并从主线程使用 Queue.get() 来更新 GUI。
【解决方案2】:

我尝试在以下测试程序中运行您的代码,并且多处理工作正常,没有任何内容被阻止:

import time
import multiprocessing as mp
import wx


def myWorker(a, b):
    time.sleep(10)
    print '{} * {} = {}'.format(a, b, a*b)

def onProcess(event):
    jobs = mp.cpu_count() * 2
    a = 5
    b = 10

    for job in range(jobs):
        mp.Process(target = myWorker, args = (a, b,)).start()

def onGUI(event):
    print 'GUI is not blocked'

class MyFrame(wx.Frame):
    def __init__(self, parent, id, title):
       wx.Frame.__init__(self, parent, id, title)
       buttons = []
       panel = wx.Panel(self, wx.ID_ANY)
       sizer = wx.BoxSizer(wx.VERTICAL)
       gui_proc_btn = wx.Button(panel, wx.ID_ANY, 'GUI Process')
       other_proc_btn = wx.Button(panel, wx.ID_ANY, 'Other process')

       gui_proc_btn.Bind(wx.EVT_BUTTON, onGUI)
       sizer.Add(gui_proc_btn, 0, wx.ALL, 5)
       other_proc_btn.Bind(wx.EVT_BUTTON, onProcess)
       sizer.Add(other_proc_btn, 0, wx.ALL, 5)
       panel.SetSizer(sizer)

class MyApp(wx.App):
    def OnInit(self):
        frame = MyFrame(None, -1, 'test.py')
        frame.Show(True)
        self.SetTopWindow(frame)
        return True

if __name__ == '__main__':

    app = MyApp(0)
    app.MainLoop()

从你的命令行运行这个,按下第二个按钮(这在你的函数上使用multiprocessing,我增加了睡眠时间),然后按下第一个按钮。 您应该注意到按下第一个按钮时会生成输出,因此程序不会被阻塞。

【讨论】:

  • 将生成输出,但在后台 mp 进程运行时尝试移动 GUI,您将收到“无响应”消息 - 至少在 Windows 中是这样。实际上,使用上面的代码,它产生了许多新的 GUI,在我将它们全部关闭之前,没有任何东西会输出到控制台。考虑到 Windows 处理多处理的方式,这种行为可能是特定于操作系统的;因此,如果您在 Linux 或其他设备上运行,您可能会看到不同的结果。谢谢!
  • 是的,我在 Linux 上。 Windows 没有分叉,因此多处理尝试将整个程序复制到内存中,也许这就是您最终拥有多个窗口的原因。
  • 糟糕,我的错。查看多处理 documentation 添加 if __name__ == '__main__' 在 windows 上是必需的,现在尝试运行代码。
  • 上次编辑后,我尝试在 Windows 上运行测试程序,它运行正常。
猜你喜欢
  • 2014-05-24
  • 2015-12-04
  • 1970-01-01
  • 1970-01-01
  • 2018-10-13
  • 2014-05-25
  • 2019-04-06
  • 2012-06-13
  • 2012-10-03
相关资源
最近更新 更多