首页 > 解决方案 > 无法终止 python 程序

问题描述

正如这里所建议的,我创建了一个单独的类,专门用于 Tkinter。主要功能只是从 Tkinter GUI 获取比例值。虽然我能够获得这些比例值,但在关闭 GUI 后我无法退出程序。self.root.mainloop()我的程序在ieprint "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)

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

标签: pythontkinterqueuetcl

解决方案


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

您从后台线程使用 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 这样的不同实现。Lock解决此问题的正确方法是使用orConditionQueueor 其他同步对象同步对要在线程之间共享的任何变量的所有访问。

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

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

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


推荐阅读