Tkinter与多进程不兼容

8
以下代码在Linux的Python 3.2.2中无法执行,程序会一直卡在那里不动:
import tkinter
from multiprocessing import Process

def f():
    root = tkinter.Tk()
    label = tkinter.Label(root)
    label.pack()
    root.mainloop()

p = Process(target=f)
p.start()

我找到的关于这个问题唯一的信息是问题 5527,其中指出问题在于在进程被fork之前导入tkinter,并且可以通过在函数f中导入tkinter来解决,而且该问题只在Linux上出现,而不是Solaris。
究竟是什么原因导致了这个问题?这是一个bug吗?除了在需要的每个地方本地导入tkinter以外,是否有其他解决方法(这不是很Pythonic)?还有其他模块是否存在类似于多进程的问题?

1
你知道这个问题。你知道已经提交了错误报告。你知道这个解决方法。唯一的其他主要问题就是“是否还有其他模块存在与多进程类似的问题?”,这似乎有点开放式。 - Steven Rumbalski
1
@StevenRumbalski:我不知道问题出在哪里 - 我不知道tkinter在这里做了什么导致无法工作,或者为什么它是平台相关的。这个错误报告已经提交了3年多,没有任何迹象表明有人知道为什么(或在什么条件下)会发生这种情况,也不知道如何修复它。也许我的最后一个问题应该是“在分叉进程之前是否有其他无法导入的标准库模块”,这更具体一些。 - James
@James,请在你的问题中更新你的操作系统和Python版本。 - Ben the Coder
4个回答

0

我怀疑问题与连接到X服务器(通常为套接字)有关。如果在进程fork()之前创建,子进程会继承此连接。但是如果它尝试使用它,X服务器会感到困惑。

在对Tkinter.py进行粗略查看后,调用启动进程 可能 有用的NoDefaultRoot函数。这完全取决于到X服务器的连接是何时建立的。

否则在分叉之后导入Tkinter似乎是正确的方式。


0
截至2013年9月,错误报告中有一些额外的评论,可以更深入地了解实际问题是什么。

http://bugs.python.org/issue5527#msg194848
http://bugs.python.org/issue5527#msg195480

基于上述内容,我猜测可能发生了以下情况:Tkinter不是线程安全的,因此(出于某种原因)Tkinter想知道哪个线程是主线程。Tkinter假设在加载Tkinter模块时的主线程也将是程序执行的主线程。当您在加载Tkinter后进行fork或多进程操作时,这种假设就会被打破。(例如,在fork之后,记住的主线程在父进程中而不是子进程中。)


0
如果您能启动一个线程,请在其后面加上root.mainloop(),它应该可以正常工作。

这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - Deenadhayalan Manoharan

0
只适用于Windows的答案: 我猜这段代码应该是可以工作的,虽然我不知道具体细节。 多个在不同进程中运行的窗口通过队列进行通信。
import tkinter
from multiprocessing import Process, Queue

def f1(q):
    root = tkinter.Tk()
    button_wid = tkinter.Button(root, text="Get Entry Text", command=lambda : get_entry(q))
    button_wid.pack()
    root.mainloop()

def get_entry(q):
    try:
        print(q.get_nowait())
    except:
        pass

def put_Q_entry_text(q, text, event=None):
    q.put(text)

def f2(q):
    root = tkinter.Tk()
    entry_wid = tkinter.Entry(root)
    entry_wid.pack()
    entry_wid.bind("<Return>", lambda event: put_Q_entry_text(q, entry_wid.get()))
    root.mainloop()

if __name__ == "__main__":
    q = Queue()
    p1 = Process(target=f1, args=(q,))
    p2 = Process(target=f2, args=(q,))
    p1.start()
    p2.start()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接