我有这个代码使用线程,但在某些时候,GUI冻结(按下按钮后)。
import threading
import Queue
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.clock import Clock
main_kv = """
<CustomLabel>:
size_hint: (None, None)
size: self.texture_size
<Main>:
orientation: "vertical"
Button:
size_hint: (1, None)
height: dp(70)
on_press: root.spawn_threads()
"""
class CustomLabel(Label):
pass
class Main(BoxLayout):
def spawn_threads(self, *args):
Clock.schedule_once(self.do_something, 1)
def job(self):
task = self.q.get()
self.add_widget(CustomLabel(text=str(task)))
self.q.task_done()
def do_something(self, *args):
data = [i for i in xrange(20)]
self.q = Queue.Queue()
for i in data:
self.q.put(i)
for _ in xrange(20):
t = threading.Thread(target=self.job)
t.daemon = 1
t.start()
self.q.join()
class TestApp(App):
def build(self):
Builder.load_string(main_kv)
return Main()
TestApp().run()
我读过某个地方,我不能阻止GUI,否则它会冻结...也许“self.q.join()”阻止了GUI。有没有其他方法来实现队列join()方法,以便我不阻止GUI?
答案 0 :(得分:1)
你实际上有两个问题。
首先,正如你猜测的那样:
我读过某个地方,我不能阻止GUI,否则它会冻结...也许“self.q.join()”阻止了GUI。有没有其他方法来实现队列join()方法,以便我不阻止GUI?
你对问题的看法是完全正确的,但你对解决方案的看法是错误的。
q.join
阻止GUI的原因是它等待所有后台工作完成。你不能在事件回调中这样做。在您的回调返回之前,整个用户界面都会被冻结,等待您。
有三种方法:
您可以生成另一个线程,只是为了等待队列,甚至是do_something
的整个主体,然后只返回而不等待该线程。毕竟,在q.join
之后你没有做任何事情,所以它发生时并不重要。
或者你根本不能等。有什么需要同步或以其他方式响应这些线程完成他们的工作?看起来不像。
最简单的说,你可以在这里使用futures
或multiprocessing.dummy
和submit
任务创建一个持久性线程池,而不是为每个创建一个新线程任务。 (您正在尝试在此处创建一个池,但是您不需要为每个操作创建一个单独的池。而且您很少需要与任务一样多的线程 - 当您这样做时,通常不需要池或队列。 )
但如果你解决了这个问题,你还会遇到另一个问题。虽然您的do_something
函数不会与UI交互,但您正在生成的任务会执行。并且您不允许从后台线程与UI进行交互。
要解决此问题,您需要将UI工作移动到@mainthread
函数:
@mainthread
def makelabel(self, text):
self.add_widget(CustomLabel(text=text))
def job(self):
task = self.q.get()
self.makelabel(str(task))
self.q.task_done()
但实际上,如果您在这些后台任务中执行的仅事情是创建窗口小部件,那么首先没有理由使用线程。你增加了一大堆开销和复杂性,并没有获得任何好处。如果您正在制作一堆网络请求,或者运行一堆子进程,或者做一些需要一段时间的其他工作并且主要是等待,那么您希望使用线程(或者再次使用线程池)。请注意@mainthread
的示例完全相同:
self.req = UrlRequest(url='http://...', on_success=callback)
(A UrlRequest
是一个致力于发出请求,等待响应,然后调用回调函数的线程。)