树莓派USB RFID应用线程无法结束:报threads can only be started once错误
嘿,我来帮你拆解下这个threads can only be started once的报错问题~
核心原因很明确:threading.Timer是个一次性的线程对象——它的start()方法只能被调用一次,一旦这个Timer执行完任务(或者被取消),就彻底“退休”了,没法再重新启动。你现在的代码里把fil定义成了全局的单个Timer实例,第一次按回车启动它没问题,但第二次触发时,这个线程已经处于终止状态,自然就报错了。
具体看你的代码问题
你提前定义了全局的Timer:
fil=threading.Timer(0.5, prova2)
然后每次按回车调用:
def activa_fil(self): print ('activant fil') fil.start()
第一次启动后,Timer在0.5秒后执行prova2,执行完毕线程就结束了。第二次再调用fil.start(),就会触发那个错误,因为这个线程已经不能再被激活了。
两种简单修复方式
方式1:每次触发时创建新的Timer
最直接的解决办法是把Timer的创建移到activa_fil函数内部,这样每次按回车都会生成一个全新的Timer对象,自然就能重复启动了:
def activa_fil(self): print ('activant fil') # 每次触发都新建一个Timer实例 fil = threading.Timer(0.5, prova2) fil.start()
记得删掉原来全局定义的fil=threading.Timer(0.5, prova2)这一行哦。
方式2:用普通线程+循环(适合持续监听场景)
如果你的RFID阅读器需要持续读取数据(而不是按回车才触发一次),可以考虑用普通线程加循环,配合事件来控制启停。不过结合你当前的代码逻辑,方式1应该更贴合你的需求。
额外小提示:Tkinter的线程安全问题
另外要注意:你的prova2函数里直接操作Tkinter的控件(比如DisplayCorr.config、Llistacorr.insert),虽然大部分时候不会出问题,但Tkinter的GUI操作不是线程安全的,严格来说应该用marc.after()来调度GUI更新,避免潜在的崩溃或异常。比如可以把GUI操作改成这样:
def prova2(): print('hola') # 先把需要的值取出来,避免跨线程访问控件 control_val = Control.get() numcorr_val = NumCorr.get() time_str = time.strftime("%H:%M:%S") print(control_val) print(numcorr_val) print(time_str) # 用after把GUI操作放到主线程执行 marc.after(0, lambda: DisplayCorr.config(text=numcorr_val)) marc.after(0, lambda: Llistacorr.insert(0, (control_val, numcorr_val, time_str))) marc.contador +=1 marc.after(0, lambda: Contador.config(text=marc.contador)) print(marc.contador) # 文件写入可以在子线程执行,没问题,用with还能自动关文件 with open("prova.txt", "a") as file: file.write(control_val) file.write(numcorr_val) file.write(time_str) file.write('\n') NumCorr.after(500, NumCorr.delete(0,END))
这样能让你的代码更健壮~
内容的提问来源于stack exchange,提问作者espluguenc

