signal.pause() 和 signal.alarm() 在非睡眠程序中导致 RecursionError

问题描述

单线程 python 程序,旨在响应来自 raspBerry pi 按钮按下的事件,也希望每分钟唤醒一次以更新 LCD 显示

主要功能

btn_1 = 21
GPIO.setup(btn_1,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(btn_1,GPIO.FALLING,callback=btn_1_press_callback,bouncetime=100)

lcd.display()
lcd.messsage("text to display on lcd"

每当按下物理按钮时,前面的代码都会运行 btn_1_press_callback 函数。 main 函数的其余部分不是在忙循环中休眠,而是执行以下操作:

signal.signal(signal.SIgalRM,wake_every_min)
signal.alarm(60)
signal.pause()

这样按钮按下时会立即发出信号。 wake_every_minute() 函数只是用当前显示的数据(从数据源更新)刷新显示,因此无论按下按钮,它都会每分钟更新一次:

def wake_every_min(sig,frame):
  lcd.clear()
  lcd.message("new string here")
  signal.alarm(60)
  signal.pause()

然后它调用signal.pause()来休眠/但再次监听信号。这很完美,除了一段时间后,我得到 RecursionError: maximum recursion depth exceeded while calling a Python object

有趣的是,它总是在同一时间,意思是“上一行重复了 482 次”总是 482:

Traceback (most recent call last):
File "./info.py",line 129,in <module>
  main()
File "./info.py",line 126,in main
  signal.pause()
File "./info.py",line 111,in wake_every_min
  signal.pause()
File "./info.py",in wake_every_min
  signal.pause()
[PrevIoUs line repeated 482 more times]

是否有另一种方法可以在没有 while True 循环和 time.sleep() 的情况下完成此操作?如果我这样做,按钮按下就没有响应,因为在最坏的情况下,总有可能出现 1.9999 分钟的延迟。

解决方法

更新:我在想这个错误。 time.sleep() 不会阻止信号发生——信号会中断 sleep()。

正确的解决方案是在主循环中休眠,永远不要调用signal.pause()。使用 SIGINT 处理程序,您还可以在按下 ^c 时立即退出:

signal.signal(signal.SIGALRM,wake_every_min)
signal.alarm(60)
signal.signal(signal.SIGINT,int_signal_handler)
while True:
    # Sleep and wait for a signal.
    sleep(10)
    signal.alarm(60)

将警报的重新设置移动到主循环中可以防止出现 RecursionError,因为新信号不会从处理程序调用堆栈中堆积起来。如果有人好奇这是为了什么,它是一个加密 LCD 代码:https://github.com/manos/crypto_lcd/blob/master/info.py