返回

Python 脚本无法 Ctrl-C 终止?三个妙招助你轻松解决!

Linux

Python 脚本无法使用 Ctrl-C 终止?这里有三个解决方案!

身处程序员的世界中,当 Python 脚本拒绝响应 Ctrl-C 命令时,这可能会成为一个令人抓狂的时刻。但别担心,我有三个法宝可以解决这个问题,让你恢复对脚本的掌控。

罪魁祸首:GIL

Python 的全球解释器锁 (GIL) 是幕后黑手。它是一种机制,一次只允许一个线程执行 Python 代码。因此,当多个线程争夺 GIL 时,它们就会形成一个队列,导致 Ctrl-C 信号被无限期地阻塞。

解决方案 1:使用 multiprocessing 模块

multiprocessing 模块提供了另一种创建线程的方式,它可以绕过 GIL 的限制。使用它,每个进程都有自己的 GIL,从而允许多个线程同时运行。

解决方案 2:使用 signal.pthread_sigmask

signal.pthread_sigmask 函数可以暂时屏蔽 SIGINT 信号(Ctrl-C 触发),直到你准备好处理它。这提供了暂时忽略 Ctrl-C 的窗口期,直到你释放屏蔽。

解决方案 3:捕获 KeyboardInterrupt 异常

Python 在接收到 Ctrl-C 时会引发 KeyboardInterrupt 异常。你可以通过在代码中捕获此异常并进行适当处理来显式地处理 Ctrl-C。

示例代码

使用 signal.pthread_sigmask

import threading
import signal

def signal_handler(signal, frame):
    print("Ctrl-C pressed")
    signal.pthread_sigmask(signal.SIG_UNBLOCK, signal.SIGINT)

def first_thread():
    while True:
        print("first")

def second_thread():
    while True:
        print("second")

if __name__ == "__main__":
    # 设置信号处理程序
    signal.signal(signal.SIGINT, signal_handler)
    # 屏蔽 SIGINT 信号
    signal.pthread_sigmask(signal.SIG_BLOCK, signal.SIGINT)

    # 启动线程
    t1 = threading.Thread(target=first_thread)
    t2 = threading.Thread(target=second_thread)
    t1.start()
    t2.start()

    # 等待线程结束
    t1.join()
    t2.join()

常见问题解答

  • 为什么我不能使用 time.sleep() 来模拟 Ctrl-C?

time.sleep() 会释放 GIL,但这并不能解除对 SIGINT 信号的阻塞。

  • 使用 multiprocessing 模块的缺点是什么?

由于进程之间通信的开销,使用 multiprocessing 模块可能会降低性能。

  • 捕获 KeyboardInterrupt 异常是否会阻止其他异常的引发?

不会,捕获 KeyboardInterrupt 异常只处理 Ctrl-C,而不影响其他异常。

  • 为什么 GIL 存在?

GIL 旨在确保 Python 解释器在多线程环境中保持线程安全。

  • GIL 有替代方案吗?

Python 3.11 中引入了多线程化(MPM),它是 GIL 的一种可选替代方案。