在编写GUI程序的时候,对于比较耗时的操作(比如按一个Button开始长时间的计算),常规的思路都是扔给线程去计算,这样可以保持GUI界面不会出现卡死。我也是这么干的!由于希望界面上的Text控件还能输出计算过程的log,我在启动线程后,调用了线程的join函数。
python创建线程并启动后,可以通过调用线程的join函数进入阻塞状态,以此等待线程结束后再往下执行。我的初衷时,tkinter界面卡死没关系,线程在运行并在输出log(界面上的Text控件),这时界面卡死也是需要的,要等线程结束才能让用户按其它按钮。
结果,我遇到了deadlock,死锁!tkinter界面也因此彻底卡死。
分析了一下界面彻底卡死的原因,原来是这样的:
创建并启动线程后,调用线程的join函数,界面卡死等待线程执行结束;此时,tkinter界面的Text控件已经被join函数锁死;线程在计算过程中,需要访问Text控件写log,因此在写log的地方开始死等。死锁!
我的代码是这样的:
def __write():
1498 try:
1499 _name, _oui, _pn, _ver,_wlen, _sp, _sn, _date = __get_cont()
1500 except ValueError as e:
1501 eblog.error(repr(e))
1502 return
1503 rth = threading.Thread(
1504 target=_write_check_vendor,
1505 args=(_name,_oui,_pn,_ver,_wlen,_sp,_sn,_date,eblog),
1506 daemon=True)
1507 rth.start()
1508 #rth.join() Deadlock: GUI is locked here, eblog is not usable.
_write_check_vendor函数要使用界面上的eblog(就是Text),__write函数对应界面上的一个按钮,点击后进入。如果在__write内调用rth.join函数,死锁!
tkinter界面彻底卡死原因找到后,就有了解决方案:
不要调用线程join函数;带来的影响只是线程在访问Text时,界面上其它按钮功能也会对Text输出,可能会造成log混乱,不过也仅仅是有点混乱而已,没有功能上的问题;
调用join,但是线程不要访问Text空间;如果可以在计算过程中不输出log,这个方案是可以的;
不调用join函数,在线程启动时关闭界面上的其它按钮,在线程结束后,再重新打开这些按钮。
GUI程序虽然使用方便,不过开发效率和执行效率确实相对较低。稍不注意,还容易在写log时界面卡死,进入死锁!要小心啊。。。
-- EOF --