В вашем коде есть несколько вещей, которые сбивают с толку, но на самом деле не ошибаются, например, тот факт, что вы скрываете свой 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]
И тогда вам не нужно беспокоиться о гонках. Таким образом, ваш код проще и гарантированно корректен.