Python, 多进程与GUI

2 投票
1 回答
5389 浏览
提问于 2025-04-18 16:33

我有一个带图形界面的程序,需要进行一些多进程处理。这样做的目的是为了避免界面卡死,让用户在处理过程中可以使用其他按钮。

我想定义一个方法,像下面这样:

def start_waiting(self, parent, func, args):

    self.window_waiting.show()

    task=Process(func(*args))
    task.start()

    task.join()

    # Some code to execute at the end of the process
    #
    #...

问题是,join()没有起作用,我需要它,因为在join()之后执行的代码可以告诉我Process什么时候结束。我会用这段代码来更新window_waiting的取消按钮。

于是我想到了另一个解决方案,避免使用join(),我把它换成了:

while task.is_alive():
    time.sleep(0.5)

但这个也没用,所以我尝试了一个计划C,就是创建一个队列:

def worker(input, output):
    for func, args in iter(input.get, 'STOP'):
        result=func(*args)
        output.put(result)


task_queue = Queue()
done_queue = Queue()
task_queue.put(task)

Process(target=worker, args=(task_queue, done_queue)).start()
done_queue.get()

最后的代码给我报了个错:'Pickling an AuthenticationString object is ' TypeError: Pickling an AuthenticationString object is disallowed for security reasons

这让我想到了一个链接,关于如何使用共享队列的多进程处理,但我还是没能解决这个问题 :/

1 个回答

2

你的第一个例子应该像这样:

def start_waiting(self,parent,func,args):

    ...not relevant code..

    self.window_waiting.show()


    task=Process(target=func, args=args)  # This line is different.
    task.start()


    task.join()

你之前的写法其实并没有在子进程中执行 func,而是在父进程中执行,然后把返回值传给了 Process。当你调用 task.start() 时,它可能会立刻失败,因为你传递的是 func 返回的结果,而不是一个函数对象。

需要注意的是,因为你在 start_waiting 中调用了 task.join(),这可能会导致你的图形界面(GUI)卡住,因为 start_waiting 不会返回,直到 func 完成,尽管它是在子进程中运行的。只有在你把 start_waiting 放在一个与 GUI 事件循环不同的线程中运行时,它才不会卡住。你可能想要的更像这样:

def start_waiting(self,parent,func,args):

    ...not relevant code..

    self.window_waiting.show() # You should interact with the GUI in the main thread only.

    self.task = Process(target=func, args=args)  # This line is different.
    self.thrd = threading.Thread(target=self.start_and_wait_for_task)
    self.thrd.start()


def start_and_wait_for_task(self):
    """ This runs in its own thread, so it won't block the GUI. """
    self.task.start()
    self.task.join()
    # If you need to update the GUI at all here, use GLib.idle_add (see https://wiki.gnome.org/Projects/PyGObject/Threading)
    # This is required to safely update the GUI from outside the main thread.

撰写回答