如何在console/rake/rails中用Ctrl-C终止含ensure块的无限递归代码
问题原因
你给出的代码无法被Ctrl+C终止的核心原因是ensure块的强制执行逻辑:
- 按下Ctrl+C时系统会向Ruby进程发送
SIGINT信号,Ruby会抛出Interrupt异常 Interrupt不属于StandardError,原代码中的rescue => err默认不会捕获该异常- 但无论异常是否被捕获、是否抛出,
ensure代码块都会优先于进程退出执行 - 你的
ensure块直接递归调用infinite方法,相当于每次触发中断后还没等进程退出,就又进入了新一轮递归,导致终止逻辑永远无法触发
解决方法
方法1:不修改原代码,运行前注册信号处理逻辑
在console/rake/rails环境中执行目标代码前,先运行以下代码注册SIGINT处理规则:
trap("SIGINT") { exit! }
之后再执行infinite方法,按下Ctrl+C就能直接强制退出进程。这里使用exit!而不是普通exit,是为了跳过所有剩余的ensure块逻辑,避免再次进入递归。
提示:如果注册了自定义SIGINT处理逻辑后要恢复默认行为,执行
trap("SIGINT", "DEFAULT")即可。
方法2:修改原代码适配Ctrl+C终止
如果可以调整原代码,单独对Interrupt异常做退出处理即可:
def infinite puts Time.now rescue Interrupt => err # 收到Ctrl-C时直接强制退出,跳过ensure块执行 exit! rescue => err puts err.message ensure infinite end
内容的提问来源于stack exchange,提问作者Ben Wiseley
相关产品推荐
相关产品推荐

