Python3 Threading问题:仅启动1个线程为何有2个活跃?如何修复?
问题原因分析
- 线程重复创建:你在
start_stop方法中没有区分启动和停止场景,无论点击Start还是Stop按钮,最后都会执行新建线程并启动的逻辑,多次点击后就会生成多个未销毁的旧线程,所以会出现Thread-1、Thread-2同时活跃的情况。 - 停止状态被回滚:当你点击Stop按钮时,分支逻辑已经把运行状态调整为停止,随后又执行了
self.running = not self.running,相当于把已经设为False的运行状态又改回了True,旧线程的while循环判断条件一直成立,自然永远不会退出。
修复代码
只需要调整start_stop方法的逻辑,停止操作仅修改运行状态通知旧线程退出,只有启动操作时才创建新线程,同时给线程设置daemon属性避免程序退出后残留线程,另外Kivy不允许子线程直接修改UI,新增Clock调度保证UI修改安全:
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from time import sleep import threading from kivy.clock import Clock # 用于跨线程安全修改UI class MyApp(BoxLayout): def __init__(self, **kwargs): super(MyApp, self).__init__(**kwargs) self.orientation = 'horizontal' self.running=False self.my_thread = None # 提前声明线程变量 self.btn = Button(text='Start', size_hint_x=.1) self.lbl = Label(size_hint_x=.9) self.add_widget(self.btn) self.btn.bind(on_press = self.start_stop) self.add_widget(self.lbl) def do_something(self,n): while self.running: # 子线程不能直接修改UI,用Clock调度到主线程执行 Clock.schedule_once(lambda dt, val=n: setattr(self.lbl, 'text', str(val)), 0) sleep(.5) n += 1 else: self.show_running_threads() def start_stop(self,event): if self.running: # 停止逻辑:只改状态,让旧线程自然退出 self.btn.text = 'Start' self.running = False else: # 启动逻辑:改状态+新建线程启动 self.btn.text = 'Stop' self.running = True # 设置daemon=True,主程序退出时线程自动销毁 self.my_thread = threading.Thread(target = self.do_something,args = (0,), daemon=True) self.my_thread.start() def show_running_threads(self): runningThreads = [] for thread in threading.enumerate(): runningThreads.append(thread.name) # 这里也用Clock调度修改UI Clock.schedule_once(lambda dt: setattr(self.lbl, 'text', 'Running threads: ' + str(runningThreads) +\ '\n my_thread running = ' + str(self.my_thread.is_alive())), 0) print(runningThreads) if self.my_thread.is_alive(): print('my_thread still running') else: print('my_thread cancelled\n','-'*80,sep='') class MyLayout(App): def build(self): return MyApp() if __name__ == '__main__': MyLayout().run()
额外说明
修改后点击Stop按钮时,self.running会被设为False,旧线程的while循环会马上终止,线程自然销毁,不会残留。只有点击Start按钮时才会创建新的线程,不会出现多个线程同时活跃的情况。
内容的提问来源于stack exchange,提问作者DaiBach
相关产品推荐
相关产品推荐

