树莓派摩尔斯电码输入程序线程无法终止问题求助
问题:树莓派摩尔斯电码程序无法通过全局变量终止按钮监听线程
我正在开发一款运行在树莓派上的摩尔斯电码输入程序,设置了3个按钮,分别对应点、划以及用于标记字母输入结束的指令键。为实现同时监听三个按钮的输入,我使用了线程技术,但希望能够停止按钮监听功能。
我尝试通过全局变量n来控制线程:按下指令键时将n设为10,本应终止所有线程中的while n == 0循环,但实际n确实被设为10后,循环仍持续运行,完全忽略n == 0的条件。我试过加入sys.exit()等方式,也调整过n的条件判断,但都没有效果,程序仍继续接收按钮输入。
以下是我的代码:
from gpiozero import Button from threading import Thread import sys btn1 = Button(2) btn2 = Button(3) cmdbtn = Button(4) currentMorse = "" def pressed1(): print("Pressed1") global currentMorse currentMorse = currentMorse + "." print(currentMorse) def released1(): print("Released") def pressed2(): print("Pressed2") global currentMorse currentMorse = currentMorse + "," print(currentMorse) def released2(): print("Released") def pressedcmd(): print("Pressedcmd") def releasedcmd(): print("Releasedcmd") global n n = 10 def checker1(): global n while n == 0: btn1.when_pressed = pressed1 btn1.when_released = released1 print(n) def checker2(): global n while n == 0: btn2.when_pressed = pressed2 btn2.when_released = released2 print(n) def checkercmd(): global n while n == 0: cmdbtn.when_pressed = pressedcmd cmdbtn.when_released = releasedcmd print(n) if __name__ == '__main__': n = 0 Thread(target = checker1).start() Thread(target = checker2).start() Thread(target = checkercmd).start()
问题分析与解决方案
错误原因
- GPIOZero回调的持续性:你在
while循环里反复设置when_pressed和when_released,但GPIOZero的回调是一次设置终身生效的,只要不主动取消,按钮事件就会一直触发,和你的线程循环完全无关。哪怕循环终止,之前设置的回调依然会响应按钮操作。 - 线程循环逻辑无效:你的
while n == 0循环里没有任何阻塞或等待逻辑,会以极高频率重复执行设置回调的操作,不仅占用CPU,还导致线程根本没机会及时读取全局变量n的最新值,循环自然无法终止。 - 全局变量控制逻辑错位:你想用全局变量终止线程循环,但核心问题是循环本身对按钮监听没有实际控制作用,就算循环终止,回调依然在运行。
修复方案
核心思路
- 放弃线程循环反复设置回调的方式,GPIOZero本身支持异步监听按钮,只需初始化设置一次回调即可。
- 用
threading.Event替代全局变量,更安全地控制线程和事件处理状态。 - 按下指令键时,不仅触发停止信号,还要主动取消所有按钮的回调,彻底终止监听。
修改后的代码示例
from gpiozero import Button from threading import Event import sys btn1 = Button(2) btn2 = Button(3) cmdbtn = Button(4) currentMorse = "" # 用Event对象实现线程安全的停止信号 stop_event = Event() def pressed1(): # 只有未停止时才处理输入 if not stop_event.is_set(): print("Pressed1") global currentMorse currentMorse += "." print(currentMorse) def released1(): if not stop_event.is_set(): print("Released") def pressed2(): if not stop_event.is_set(): print("Pressed2") global currentMorse currentMorse += "-" # 改用"-"表示划,符合摩尔斯电码规范 print(currentMorse) def released2(): if not stop_event.is_set(): print("Released") def pressedcmd(): if not stop_event.is_set(): print("Pressedcmd") def releasedcmd(): print("Releasedcmd") # 触发停止信号 stop_event.set() # 取消所有按钮的回调,彻底终止监听 btn1.when_pressed = None btn1.when_released = None btn2.when_pressed = None btn2.when_released = None cmdbtn.when_pressed = None cmdbtn.when_released = None print("已停止按钮监听") # 仅初始化一次回调即可 btn1.when_pressed = pressed1 btn1.when_released = released1 btn2.when_pressed = pressed2 btn2.when_released = released2 cmdbtn.when_pressed = pressedcmd cmdbtn.when_released = releasedcmd if __name__ == '__main__': try: # 主程序阻塞等待停止信号 stop_event.wait() except KeyboardInterrupt: # 处理Ctrl+C强制退出的情况 stop_event.set() # 清理回调 btn1.when_pressed = None btn1.when_released = None btn2.when_pressed = None btn2.when_released = None cmdbtn.when_pressed = None cmdbtn.when_released = None print("程序已退出")
关键改进点
- 用
threading.Event替代全局变量,线程间状态同步更可靠。 - 每个回调函数先检查停止状态,避免停止后仍处理按钮事件。
- 按下指令键时主动取消所有回调,彻底终止按钮监听。
- 主程序通过
stop_event.wait()保持运行,同时支持Ctrl+C安全退出。
内容的提问来源于stack exchange,提问作者Bren
相关产品推荐
相关产品推荐

