tkinter界面因线程join卡死的案例

2019年12月26日 / 395次阅读 / Last Modified 2019年12月28日
tkinter多线程

在编写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 --

本文链接:https://www.pynote.net/archives/1633

留言区

《tkinter界面因线程join卡死的案例》有6条留言

电子邮件地址不会被公开。 必填项已用*标注

  • 麦新杰

    移动窗口时,右键不松开,此时tkinter的GUI也是卡死状态!额... [回复]

  • 麦新杰

    本文卡死是因为在独立的线程中更新GUI的widget,在Google,Stackoverflow上基本都是不推荐子线程里更新GUI。不知道tkinter的Text控件,是不是线程安全的? [回复]

    • 麦新杰

      写了个测试程序,多线程不停地写Text空间,未见输出混乱。 [回复]

  • 麦新杰

    在__write中直接操作eblog,他们在同一个线程中,不会卡死,只是Text的显示有些滞后,表现为所有的显示在最后一刻突然出现。但在另一个线程中操作eblog,由于__write函数中有join阻塞,再加上对eblog的调用不会返回,就出现了死锁。问题是:为什么在另一个线程操作eblog,它就不返回了呢? [回复]

    • 麦新杰

      python解释器能判断,对tkinter界面上各个控件的操作,是在同一线程中,还是别的线程。在同一线程中,不阻塞,这是合理的设计。点击功能按钮,界面要有所反应,如果界面其它部分也不返回,一点就卡死了!而对于别的线程的访问,阻塞,按照代码流程执行。 [回复]

  • 麦新杰

    GUI是一个单线程,点击Button后不返回,其它对界面元素的操作,都会卡死,包括定时器after中的操作。 [回复]


前一篇:
后一篇:

More


©Copyright 麦新杰 Since 2019 Python笔记

go to top