wxPython non-blocking GUI threading AND multiprocessing?

若如初见. 提交于 2019-12-19 11:16:41

问题


Python 2.7.3 x64 wxPython 2.8 x64

Been reading quite a bit on python threading and multiprocessing, particularly some articles by Doug Hellmann, which have helped tremendously. However, I'm confused about one thing...

I thought the Python multiprocessing module was more-or-less a drop-in replacement for the threading module, excepting that args must be picklable, but I'm finding that in order not to block my GUI, I must first create a new thread with threading.Thread then multiprocess within that thread with multiprocessing.Process. This works, and works well, but it seems a bit kludgey to me.

If I try to directly multiprocess without first threading, then my GUI still blocks until the multiprocessing job is done. Is that working as designed, or am I missing something fundamental about the multiprocessing module?

If examples are needed, I can provide them.

Thanks,

-RMWChaos

An example was requested...

Assuming that onProcess() is triggered by a button in the GUI, this blocks the 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()

While this doesn't...

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()

回答1:


Multi-threading and multi-processing are fundamentally different.

Threads are used for i/o for the most part. When you make a new thread it is contained within the same process as the program you spawned the thread from. What that means is that it shares memory space with the program, but they (the program and the thread) cannot run in parallel (also look up GIL).

Multi-processing on the other hand spawns a new process on the OS level. This new process can run in parallel with the pre-existing process, but it does not share memory space with the program that spawned it. This is more useful when the code you want to speed up is not i/o related but actual processor intensive computation.

For a gui, you mostly want to use threading for different parts of the gui so that running something in one part of the gui does not lock up your entire gui until that handling ends.

Without code it is hard to tell, but I believe you should not particularly need to spawn a process within the new thread, but instead just have that thread handle the processing. However, if that thread needs to handle intensive computational type of processing as opposed to lots of i/o, then you would want to start a process to handle that.

I believe that most of your problem lies in misunderstanding multi-processing vs multithreading.




回答2:


I tried running your code in the following test program and multiprocessing works fine, nothing is blocked:

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()

Run this from your command-line, press the second button(this uses multiprocessing on your function, and I increased the sleep time), and then press the first button. you should notice that there's output generated when pressing the first button so the program is not blocked.



来源:https://stackoverflow.com/questions/10935515/wxpython-non-blocking-gui-threading-and-multiprocessing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!