树莓派技术问询:如何通过Ctrl-C终止LED闪烁线程?
解决Raspberry Pi LED闪烁程序无法用Ctrl-C终止的问题
你的问题核心在于线程的默认行为和主线程的生命周期,下面一步步拆解原因和解决方案:
为什么Ctrl-C没用?
- 你的主线程在启动所有子线程后就直接退出了,但默认创建的
Thread是非守护线程——这意味着即使主线程结束,子线程会继续运行,进程也会保持存活。 - 当你按下
Ctrl-C时,信号会发送给整个进程,但此时主线程已经没有运行中的代码来捕获KeyboardInterrupt异常,子线程里的while True循环会一直跑下去,导致程序无法终止。
可行的解决思路和修改方案
1. 将子线程设为守护线程
把线程设置为守护线程后,主线程一旦退出,所有守护线程会被强制终止。修改你的start函数:
def start(port, hz): t = Thread(target=blink, args=(port, hz)) t.daemon = True # 标记为守护线程 t.start()
2. 让主线程保持存活并捕获中断信号
主线程需要一直运行,才能接收到Ctrl-C触发的KeyboardInterrupt,然后我们可以在异常处理里清理GPIO资源:
在所有start()调用后添加这段代码:
try: # 让主线程保持运行,等待中断信号 while True: time.sleep(1) except KeyboardInterrupt: print("收到终止信号,正在清理GPIO...") GPIO.cleanup() # 重置GPIO状态,避免引脚处于异常状态
3. (可选)更优雅的线程终止方式(用事件控制)
如果不想强制终止线程,可以用threading.Event来给子线程发送终止信号,替代while True:
from threading import Thread, Event # 全局终止事件 stop_event = Event() def blink(port, hz): GPIO.setup(port, GPIO.OUT) pulse = 0.5/hz dtm = time.time() while not stop_event.is_set(): # 检查终止信号 dtm+= pulse time.sleep(dtm - time.time()) if GPIO.input(32) == 1: GPIO.output(port, not GPIO.input(port)) else: GPIO.output(port, GPIO.LOW) GPIO.output(port, GPIO.LOW) # 终止前关闭LED # 修改start函数传递事件 def start(port, hz): t = Thread(target=blink, args=(port, hz)) t.start() # 主线程处理中断 try: while True: time.sleep(1) except KeyboardInterrupt: print("正在终止所有线程...") stop_event.set() # 发送终止信号给所有子线程 # 等待所有子线程结束 for thread in Thread.enumerate(): if thread != Thread.current_thread(): thread.join() GPIO.cleanup()
完整修改后的代码(基础版)
#!/usr/bin/python import RPi.GPIO as GPIO import time from threading import Thread GPIO.setmode(GPIO.BOARD) GPIO.setup(32, GPIO.IN) #LED Blinking Function def blink(port, hz): GPIO.setup(port, GPIO.OUT) pulse = 0.5/hz dtm = time.time() while True: dtm+= pulse time.sleep(dtm - time.time()) if GPIO.input(32) == 1: GPIO.output(port, not GPIO.input(port)) else: GPIO.output(port, GPIO.LOW) #to make it easier to add new LED def start(port, hz): t = Thread(target=blink, args=(port, hz)) t.daemon = True t.start() #to add LED insert start(GPIOport, Hz) start(15, 2) start(16, 2) start(18, 2) start(22, 2) start(29, 2) # 主线程保持运行并捕获中断 try: while True: time.sleep(1) except KeyboardInterrupt: print("Terminating program...") GPIO.cleanup()
现在按下Ctrl-C,主线程会捕获中断,清理GPIO资源,同时所有守护子线程会随主线程一起终止,程序就能正常退出了。
内容的提问来源于stack exchange,提问作者Kai
相关产品推荐
相关产品推荐

