【问题标题】:Cannot 'pickle' a Tkinter object in a multiprocessing environment in Windows无法在 Windows 的多处理环境中“腌制”Tkinter 对象
【发布时间】:2014-11-10 00:22:52
【问题描述】:

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

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

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(我正在开发我的应用程序)时,它没有给我错误:

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。

【问题讨论】:

    标签: python windows python-2.7 tkinter multiprocessing


    【解决方案1】:

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

    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 来告诉子进程中的循环从父进程中停止。

    另外,无论如何,在 Linux 上运行原始代码实际上会导致解释器崩溃:

    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.
    

    【讨论】:

    • 您编写的代码存在问题,当它关闭时,即调用 process.stop() 时会引发错误:“AttributeError: 'Subprocess' object has no attribute' root'" 这很奇怪,因为在 run 函数中设置了 self.root 属性
    • @trvrrp 现已修复。抱歉,我最初错过了stop 的电话。您无法从父进程访问root,因为该属性仅存在于子进程中。您必须使用multiprocessing.Queue 来告诉孩子终止主循环。
    • 有没有办法将目标函数传递给该子进程?如果它可以处理参数和返回会更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 2019-02-19
    • 2019-03-03
    • 2020-09-22
    • 2022-01-02
    • 2015-11-07
    • 2020-04-07
    相关资源
    最近更新 更多