【问题标题】:Unable to terminate a python program无法终止 python 程序
【发布时间】:2018-09-06 02:26:04
【问题描述】:

按照here 的建议,我创建了一个单独的类,专门用于 Tkinter。主要功能只是从 Tkinter GUI 获取比例值。虽然我能够获得这些比例值,但在关闭 GUI 后我无法退出程序。我的程序似乎在self.root.mainloop()print "mainloop" 被执行后被卡住了。如果我执行以下操作,即如果我不访问 output_q 值,我在终止脚本时没有任何问题

i = 0
while app.run:
 print i
 i = i+1

下面给出完整的代码

from Tkinter import *
import threading, time, sys, Queue

class App(threading.Thread):

    def __init__(self, var):
        threading.Thread.__init__(self) 
        self.output_q = Queue.Queue()      
        self.start()

    def callback(self):
        self.run = 0
        self.root.quit()
        self.root.destroy()

    def pub_y(self, val_y):  
        self.x_val = float(self.y_scale.get())
        self.output_q.put((self.x_val, 2, 3)) 

    def run(self):

        self.root = Tk()
        self.root.protocol("WM_DELETE_WINDOW", self.callback)

        self.y_var = DoubleVar()             
        self.y_scale = Scale( self.root, from_=0, to=1, length=300, label="yaw", resolution=0.0000000000001, variable = self.y_var, orient=HORIZONTAL, command=self.pub_y)
        self.y_scale.set(0.5)
        self.y_scale.pack(anchor=CENTER)

        label = Label(self.root, text="Hello World")
        label.pack()

        self.root.mainloop()
        print "mainloop"

var = 1
input = Queue.Queue()
input.put((1,2,3))
app = App(input)
i = 0  
while app.run:
 result = app.output_q.get()
 print result[0]

sys.exit(0)

有人能指出我可能出错的地方吗?谢谢!

【问题讨论】:

    标签: python tkinter queue tcl


    【解决方案1】:

    您的代码有很多令人困惑但实际上并没有错的地方,例如,您使用值为 0 的实例属性隐藏了 run 方法……

    您从后台线程使用 tkinter 的事实实际上是错误的,并且可能导致在某些平台上挂起。但这可能不是这里的原因。

    您的实际问题是队列逻辑。想想它是如何工作的:

    • 您的主线程检查线程是否正在运行。
    • 您的主线程调用output_q.get(),它会永远阻塞,直到有东西被放入队列。
    • 您的后台线程收到一个销毁事件。
    • 您的后台线程设置run = 0 并退出。
    • 您的主线程仍在队列中永远等待,因此它永远不会看到 run 现在是错误的,因此它永远不会退出。

    解决此问题的一种方法是使用您已经使用的同一个队列来唤醒主线程,以便它可以看到您已经退出:

    def callback(self):
        self.run = 0
        self.root.quit()
        self.root.destroy()
        self.output_q.put(None)
    

    当然现在你不能只使用result[0],因为result可能是None,所以你需要添加:

    if result is not None:
    

    但仍然存在一个问题:您在一个线程中更改的变量保证最终对其他线程可见,但不是立即。如果主线程在再次阻塞队列之后才看到更改,会发生什么?它永远卡住了。

    我很确定这将在 CPython 上工作,但这不能保证,它只是碰巧是真的,它可能在 Jython 等不同的实现上工作。解决此问题的正确方法是使用 LockConditionQueue 或其他同步对象来同步对要在线程之间共享的任何变量的所有访问。

    但请注意,您实际上不再需要while app.run: 测试,因为您可以这样做:

    while True:
        result = app.output_q.get()
        if result is None:
            break
        print result[0]
    

    然后您就不必担心比赛了。这样,您的代码更简单,并且保证正确。

    【讨论】:

    • @abarnet 感谢您的详细回复。我同意,不需要self.run = 0。后台线程是什么意思?我以前没有使用过队列,所以我可能是错的,但是 while app.run: 条件正在检查中,对吗?所以,如果这个值在另一个线程中为假,它应该自动退出循环。您能告诉我有关“同步对象”的信息吗?
    • 顺便说一句,我按照上述进行了更改,并在终止时给了我以下错误,Exception RuntimeError: 'main thread is not in main loop' in <bound method DoubleVar.__del__ of <Tkinter.DoubleVar instance at 0x7f86a746d7e8>> ignored Tcl_AsyncDelete: async handler deleted by the wrong thread Aborted (core dumped)
    • 我关注了this。似乎没有问题。
    • 我还在我的系统上运行了same 代码,没有任何错误。所以这不是平台问题,而是代码本身的问题。
    • 如果我在回调函数中使用sys.exit(0)会怎样?在这种情况下,脚本应该在回调本身中退出,对吗?不管result = app.output_q.get()
    猜你喜欢
    • 2016-11-22
    • 1970-01-01
    • 2012-10-12
    • 1970-01-01
    • 2022-11-27
    • 1970-01-01
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    相关资源
    最近更新 更多