Reputation: 3217
I'm hoping someone can explain this behavior to me. If I import a module that starts a wxpython interface, threads are unable to start until after the app.MainLoop() ends. Simplest example:
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
If you run simple_app.py by itself it works fine, if you run main.py the thread never starts... Why? I have a feeling it has to do with the thread being unable to secure a lock.
Upvotes: 3
Views: 460
Reputation: 9220
The second thread in simple_app.py
is trying to import the time
module while an import is already running, which leads to deadlock when simple_app
is being imported from the main module. Because imports acquire interpreter's import lock for the current thread while importing a module. It has been documented.
Threads in the main module can import other modules that's why running simple_app.py
as main module works. Moving from time import sleep
to module level in simple_app.py
solves the problem.
Running the following code helps better understand the problem;
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()
Upvotes: 2
Reputation: 3177
This was a quite interesting problem. The issue at hand was not quite obvious (at least to me).
The last line of simple_app.py blocks until frame
is closed/destroyed. Therefore if started from main.py the import will finish only (and show output from print), when the frame is closed.
Try the following instead (normally you would structure your program better to start/stop the app where you need it):
In simple_app.py change last line to:
if __name__ == '__main__':
app.MainLoop()
In main.py
import wx
import simple_app
app = wx.GetApp()
app.MainLoop()
I can not tell you why exactly there is a difference on running directly and on importing (running directly shows the result of print, but importing does not).
Upvotes: 1