如果未从主模块调用 wx.app.mainloop,则线程无法启动

thread fails to start if wx.app.mainloop isn't called from the main module

我希望有人能向我解释这种行为。如果我导入一个启动 wxpython 接口的模块,线程在 app.MainLoop() 结束之前无法启动。最简单的例子:

simple_app.py

import wx
from threading import Thread

def test():
    from time import sleep
    while 1:
        print("thread still running")
        sleep(2)

app = wx.App() 
frame = wx.Frame(None, -1, 'simple.py')
frame.Show()
thread = Thread(target=test)
thread.setDaemon(True)
thread.start()
app.MainLoop()

main.py

import simple_app

如果你 运行 simple_app.py 本身它工作正常,如果你 运行 main.py 线程永远不会启动......为什么?我感觉这与线程无法获得锁有关。

这是一个很有趣的问题。手头的问题不是很明显(至少对我而言)。

simple_app.py 的最后一行阻塞直到 frame 是 closed/destroyed。因此,如果从 main.py 开始,导入将仅在框架关闭时完成(并显示打印输出)。

改为尝试以下方法(通常您会更好地构建程序以 start/stop 您需要的应​​用程序):

simple_app.py 中将最后一行更改为:

if __name__ == '__main__':
    app.MainLoop()

main.py

import wx
import simple_app
app = wx.GetApp()
app.MainLoop()

我不能告诉你为什么 运行 直接和导入有区别(运行 直接显示打印结果,而导入没有)。

simple_app.py 中的第二个线程正在尝试导入 time 模块,而导入已经 运行,这导致在从 simple_app 导入时出现死锁主模块。因为导入在导入模块时获取当前线程的解释器导入锁。已经 documented

主模块中的线程可以导入其他模块,这就是 运行 simple_app.py 作为主模块工作的原因。将 from time import sleep 移动到 simple_app.py 中的模块级别可以解决问题。

运行下面的代码有助于更好地理解问题;

my_time.py
from time import sleep

simple_app.py

import imp
import sys
from threading import Thread
from time import sleep

import wx

class MyFinder(object):
    def __init__(self):
        print('MyFinder initializing')
        if imp.lock_held():
            while imp.lock_held():
                print('import lock held')
                sleep(2)
            print('import lock released')
        else:
            print('import lock is not held')

    def find_module(self, module, package=None):
        print('MyFinder.find_module called with module name "{}", pakcage name "{}"'.format(module, package))
        return None


def test():
    sys.meta_path.append(MyFinder())
    from my_time import sleep
    count = 0
    while True:
        print("{} thread still running".format(count))
        count += 1
        sleep(2)


app = wx.App()
frame = wx.Frame(None, -1, 'simple.py')
frame.Show()
thread = Thread(target=test)
thread.setDaemon(True)
thread.start()
app.MainLoop()