Python kivy应用程序在使用线程时冻结

Python kivy应用程序在使用线程时冻结,python,kivy,Python,Kivy,我有这个使用线程的代码,但在某个时候,GUI冻结(在我按下按钮后) 导入线程 导入队列 从kivy.app导入应用程序 从kivy.lang导入生成器 从kivy.uix.boxlayout导入boxlayout 从kivy.uix.label导入标签 从kivy.clock导入时钟 主“kv=”“” : 大小提示:(无,无) 大小:self.texture\u大小 : 方向:“垂直” 按钮: 大小提示:(1,无) 高度:dp(70) 按:root.spawn\u threads() """ 类

我有这个使用线程的代码,但在某个时候,GUI冻结(在我按下按钮后)

导入线程
导入队列
从kivy.app导入应用程序
从kivy.lang导入生成器
从kivy.uix.boxlayout导入boxlayout
从kivy.uix.label导入标签
从kivy.clock导入时钟
主“kv=”“”
:
大小提示:(无,无)
大小:self.texture\u大小
:
方向:“垂直”
按钮:
大小提示:(1,无)
高度:dp(70)
按:root.spawn\u threads()
"""
类别自定义标签(标签):
通过
主类(箱布局):
def spawn_线程(self,*args):
时钟。计划一次(self.do\u something,1)
def作业(自我):
task=self.q.get()
self.add_小部件(CustomLabel(text=str(task)))
self.q.任务完成()
def do_something(self,*args):
数据=[i代表x范围内的i(20)]
self.q=Queue.Queue()
对于数据中的i:
自我问责(一)
对于X范围(20)内的uu:
t=线程.Thread(目标=self.job)
t、 守护进程=1
t、 开始()
self.q.join()
类TestApp(应用程序):
def生成(自):
建筑商负载串(主千伏)
返回主管道()
TestApp().run()

我在某个地方读到,我不能阻止GUI,否则它会冻结。。。可能是“self.q.join()”阻塞了GUI。有没有其他方法来实现queue join()方法,这样我就不会阻塞GUI?

这里实际上有两个问题


首先,正如你所猜测的:

我在某个地方读到,我不能阻止GUI,否则它会冻结。。。可能是“self.q.join()”阻塞了GUI。有没有其他方法来实现queue join()方法,这样我就不会阻塞GUI

你对这个问题的看法是绝对正确的,但你对解决方案的看法是错误的

q.join
阻止GUI的原因是它正在等待您的所有后台工作完成。在事件回调中不能这样做。在您的回调返回之前,整个UI都将冻结,等待着您

有三种解决方法:

  • 您可以生成另一个线程来等待队列,或者甚至执行整个
    dou_something
    ,然后返回而不等待该线程。毕竟,在
    q.join
    之后,您没有做任何事情,所以它何时发生并不重要

  • 或者你根本就不能等。在这些线程完成工作时,是否需要同步或以其他方式响应这些线程?看起来不是这样

  • 最简单的是,您可以使用或创建一个持久线程池,然后在此处向其提交任务,而不是为每个任务创建一个新线程。(您试图在此处创建一个池,但不需要为每个操作创建一个单独的池。而且您很少需要像任务那样多的线程,通常不需要池或队列。)


但如果你解决了这个问题,你还有另一个问题。虽然您的
do\u something
函数与UI不交互,但您正在生成的任务会执行。不允许您从后台线程与UI交互

要修复此问题,您需要将UI工作移动到函数中:

@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是一个线程,专门用于发出请求、等待响应,然后调用回调函数。)

这里实际上有两个问题


首先,正如你所猜测的:

我在某个地方读到,我不能阻止GUI,否则它会冻结。。。可能是“self.q.join()”阻塞了GUI。有没有其他方法来实现queue join()方法,这样我就不会阻塞GUI

你对这个问题的看法是绝对正确的,但你对解决方案的看法是错误的

q.join
阻止GUI的原因是它正在等待您的所有后台工作完成。在事件回调中不能这样做。在您的回调返回之前,整个UI都将冻结,等待着您

有三种解决方法:

  • 您可以生成另一个线程来等待队列,或者甚至执行整个
    dou_something
    ,然后返回而不等待该线程。毕竟,在
    q.join
    之后,您没有做任何事情,所以它何时发生并不重要

  • 或者你根本就不能等。在这些线程完成工作时,是否需要同步或以其他方式响应这些线程?看起来不是这样

  • 最简单的是,您可以使用或创建一个持久线程池,然后在此处向其提交任务,而不是为每个任务创建一个新线程。(您试图在此处创建一个池,但不需要为每个操作创建一个单独的池。而且您很少需要像任务那样多的线程,通常不需要池或队列。)


但如果你解决了这个问题,你还有另一个问题。虽然您的
do\u something
函数与UI不交互,但您正在生成的任务会执行。不允许您从后台线程与UI交互

要修复此问题,您需要将UI工作移动到函数中:

@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()

但实际上,如果你在这些后台任务中唯一要做的就是创建一个小部件,那么首先就没有理由使用线程。你在加一个面包