无法在 Windows 的多处理环境中“腌制"Tkinter 对象 [英] Cannot 'pickle' a Tkinter object in a multiprocessing environment in Windows

查看:65
本文介绍了无法在 Windows 的多处理环境中“腌制"Tkinter 对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个应用程序,其中 Tkinter GUI 由其他持续获取数据的对象更新.我在使用多线程时遇到问题,所以我决定尝试使用多处理模块.

I am trying to create an application where a Tkinter GUI is updated by other objects that are continuously taking data. I was having issues using multithreading so I decided to try and use the multiprocessing module.

我发现您无法在 multiprocessing.Process 内运行 Tkinter 窗口,以下是最小示例:

I've found that you cannot run a Tkinter window inside of a multiprocessing.Process, here is the minimum example:

import Tkinter as tk
import multiprocessing

class Subprocess(multiprocessing.Process):
    def __init__(self):
        multiprocessing.Process.__init__(self)
        self.root = tk.Tk()
    #

    def run(self):
        self.root.mainloop()
    #

    def stop(self):
        self.root.destroy()
        self.terminate()


if __name__ == '__main__':
        process = Subprocess()
        process.start()
        print "I got around the global interpreter lock"
        raw_input()
        print "exiting"
        process.stop()

我期望发生的是弹出一个 Tk 窗口,并在终端中显示我绕过了全局解释器锁".我在 ubuntu linux 上对此进行了测试,它运行良好,但是当我切换到 Windows 7(我正在开发应用程序的地方)时,它未能给我错误:

What I expect to happen is for a Tk window to pop up and "I got around the global interpreter lock" to show up in the terminal. I tested this out on ubuntu linux and it worked fine, but when I switched over to Windows 7 (where I am developing my application) it failed giving me the error:

Traceback (most recent call last):
  File "C:\pathtoscript\multiprocessing_test.py", line 21, in <module>
    process.start()
  File "C:\Python27\lib\multiprocessing\process.py", line 130, in start
    self._popen = Popen(self)
  File "C:\Python27\lib\multiprocessing\forking.py", line 277, in __init__
    dump(process_obj, to_child, HIGHEST_PROTOCOL)
  File "C:\Python27\lib\multiprocessing\forking.py", line 199, in dump
    ForkingPickler(file, protocol).dump(obj)
  File "C:\Python27\lib\pickle.py", line 224, in dump
    self.save(obj)
  File "C:\Python27\lib\pickle.py", line 331, in save
    self.save_reduce(obj=obj, *rv)
  File "C:\Python27\lib\pickle.py", line 419, in save_reduce
    save(state)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python27\lib\pickle.py", line 681, in _batch_setitems
    save(v)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\pickle.py", line 725, in save_inst
    save(stuff)
  File "C:\Python27\lib\pickle.py", line 286, in save
    f(self, obj) # Call unbound method with explicit self
  File "C:\Python27\lib\pickle.py", line 649, in save_dict
    self._batch_setitems(obj.iteritems())
  File "C:\Python27\lib\pickle.py", line 681, in _batch_setitems
    save(v)
  File "C:\Python27\lib\pickle.py", line 313, in save
    (t.__name__, obj))
PicklingError: Can't pickle 'tkapp' object: <tkapp object at 0x02BD3D08>

有人知道解决方法吗?这对我来说似乎很奇怪,这适用于 linux 但不适用于 Windows.

Does anyone know a workaround to this? It seems odd to me that this works on linux but not on Windows.

推荐答案

这是一个简单的修复 - 只需在子进程中创建 tkapp 对象,而不是在父进程中:

This is a simple fix - Just create the tkapp object in the child process, rather than the parent:

import Tkinter as tk
import multiprocessing
from Queue import Empty

class Subprocess(multiprocessing.Process):
    def __init__(self):
        multiprocessing.Process.__init__(self)
        self.queue = multiprocessing.Queue()
    #

    def run(self):
        self.root = tk.Tk()
        self.root.after(100, self._check_queue) # Check the queue every 100ms
        self.root.mainloop()

    def _check_queue(self):
        try:
            out = self.queue.get_nowait()
            if out == 'stop':
                self.do_stop()
                return
            # Could check for other commands here, too
        except Empty:
            pass
        self.root.after(100, self._check_queue)

    def stop(self):
        self.queue.put('stop')

    def do_stop(self):
        self.root.destroy()


if __name__ == '__main__':
    process = Subprocess()
    process.start()
    print "I got around the global interpreter lock"
    raw_input()
    print "exiting"
    process.stop()

尝试在父级中创建 tkapp,然后在子级中开始,这不是一个可行的解决方案.唯一棘手的部分是您需要使用 Queue 来告诉子进程中的循环从父进程停止.

Trying to create the tkapp in the parent, but then start in the child, isn't going to be a workable solution. The only tricky part is that you need to use a Queue to tell the loop in the child process to stop from the parent.

此外,就其价值而言,在 Linux 上运行原始代码实际上会使解释器崩溃:

Also, for what its worth, running the original code on Linux actually crashes the interpreter for me:

XIO:  fatal IO error 11 (Resource temporarily unavailable) on X server ":0"
      after 87 requests (87 known processed) with 0 events remaining.
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: ../../src/xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.

这篇关于无法在 Windows 的多处理环境中“腌制"Tkinter 对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆