Невозможно завершить программу Python - PullRequest
0 голосов
/ 06 сентября 2018

Как указано выше здесь , я создал отдельный класс, специально для Tkinter. Основная функция просто получает значения шкалы из графического интерфейса Tkinter. Хотя я могу получить эти значения шкалы, я не могу выйти из программы после закрытия графического интерфейса. Кажется, моя программа зависла после выполнения 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)

Может ли кто-нибудь указать, где я могу пойти не так? Спасибо!

1 Ответ

0 голосов
/ 06 сентября 2018

В вашем коде есть несколько вещей, которые сбивают с толку, но на самом деле не ошибаются, например, тот факт, что вы скрываете свой run метод с помощью атрибута экземпляра со значением 0…

Тот факт, что вы используете 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 или Condition или Queue или другой объект синхронизации.

Но обратите внимание, что вам на самом деле не нужно while app.run: тест больше, потому что вы можете просто сделать это:

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

И тогда вам не нужно беспокоиться о гонках. Таким образом, ваш код проще и гарантированно корректен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...