python - 在线程中捕获键盘中断或处理信号

标签 python multithreading python-3.x signals

我有一些线程正在运行,其中一个线程包含一个将生成子进程的对象。我希望这样的一个子进程能够杀死整个应用程序。上述对象在收到此信号时需要保存一些状态。不幸的是,我无法在导致终止的线程中处理要处理的信号。

下面是一些尝试复制这种情况的示例代码。

parent.py:启动一个线程。该线程运行一些子进程,其中一个子进程将尝试终止父进程。

#!/usr/local/bin/python3
import subprocess, time, threading, random

def killer_func():
    possible_cmds = [['echo', 'hello'],
                     ['echo', 'world'],
                     ['/work/turbulencetoo/tmp/killer.py']
                     ]
    random.shuffle(possible_cmds)
    for cmd in possible_cmds:
        try:
            time.sleep(2)
            subprocess.check_call(cmd)
            time.sleep(2)
        except KeyboardInterrupt:
            print("Kill -2 caught properly!!")
            print("Here I could properly save my state")
            break
        except Exception as e:
            print("Unhandled Exception: {}".format(e))
        else:
            print("No Exception")

killer_thread = threading.Thread(target=killer_func)
killer_thread.start()
try:
    while True:
        killer_thread.join(4)
        if not killer_thread.is_alive():
            print("The killer thread has died")
            break
        else:
            print("Killer thread still alive, try to join again.")
except KeyboardInterrupt:
    print("Caught the kill -2 in the main thread :(")

print("Main program shutting down")

killer.py,一个尝试使用 SIGINT 终止其父进程的简单程序:

#!/usr/local/bin/python3
import time, os, subprocess, sys

ppid = os.getppid()

# -2 specifies SIGINT, python handles this as a KeyboardInterrupt exception
cmd = ["kill", "-2", "{}".format(ppid)]

subprocess.check_call(cmd)
time.sleep(3)

sys.exit(0)

以下是运行父程序的一些示例输出:

$ ./parent.py
hello
Killer thread still alive, try to join again.
No Exception
Killer thread still alive, try to join again.
Caught the kill -2 in the main thread :(
Main program shutting down
No Exception
world
No Exception

我尝试在 killer_func 中使用 signal.signal(),但它在子线程中不起作用。

有没有办法在主线程不知道的情况下强制函数处理信号或异常?

最佳答案

程序的主线程始终是接收信号的线程。 signal module documentation声明如下:

Some care must be taken if both signals and threads are used in the same program. The fundamental thing to remember in using signals and threads simultaneously is: always perform signal() operations in the main thread of execution. Any thread can perform an alarm(), getsignal(), pause(), setitimer() or getitimer(); only the main thread can set a new signal handler, and the main thread will be the only one to receive signals (this is enforced by the Python signal module, even if the underlying thread implementation supports sending signals to individual threads). This means that signals can’t be used as a means of inter-thread communication. Use locks instead.

您需要重构您的程序,以便接收信号的主线程不会阻止您保存状态。最简单的方法是使用诸如 threading.Event() 之类的东西来告诉后台线程程序已被中止,并让它在看到事件已设置时进行清理:

import subprocess
import threading
import random

def killer_func(event):
    possible_cmds = [['echo', 'hello'],
                     ['echo', 'world'],
                     ['/home/cycdev/killer.py']
                     ]
    random.shuffle(possible_cmds)
    for cmd in possible_cmds:
        subprocess.check_call(cmd)
        event.wait(4)
        if event.is_set():
            print("Main thread got a signal. Time to clean up")
            # save state here.
            return

event = threading.Event()
killer_thread = threading.Thread(target=killer_func, args=(event,))
killer_thread.start()
try:
    killer_thread.join()
except KeyboardInterrupt:
    print("Caught the kill -2 in the main thread :)")
    event.set()
    killer_thread.join()

print("Main program shutting down")

关于python - 在线程中捕获键盘中断或处理信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30267647/

相关文章:

python - 在 Python 中生成 Chebyshev 多项式的系数

python - BCBio 的 GFF 解析器解析不正确

c - 错误 : passing argument 1 of ‘kthread_create_on_node’ from incompatible pointer type

multithreading - 复制工作线程的字符串

Android picasso 多线程

python - 无法确定 turtle 运动的时间

regex - Python 3 : Splitting by\x0 as a delimiter in a string

python - 无法连接到Docker中的Elasticsearch

Python:如何在我的时间序列中删除每天的前 5 分钟?

python - 无法从模板中的 for 循环中获得我想要的内容?