我有一些线程正在运行,其中一个线程包含一个将产生子进程的对象。我希望一个这样的子进程能够杀死整个应用程序。上述对象在接收到该信号时需要保存一些状态。不幸的是,我无法在导致杀死的线程中处理信号。
以下是一些尝试复制情况的示例代码。
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
我尝试在signal.signal()
中使用killer_func
,但它在子线程中不起作用。
有没有办法强制函数处理信号或异常,而主线程不知道?
答案 0 :(得分:1)
程序的主线程始终是接收信号的线程。 signal
module documentation说明了这一点:
如果使用信号和线程,必须要小心 同样的计划。使用信号和记忆时要记住的基本要点 线程同时是:始终执行
signal()
操作 主要执行线程。任何线程都可以执行alarm()
,getsignal()
,pause()
,setitimer()
或getitimer()
;只有主线程 可以设置一个新的信号处理程序,,主线程将是唯一的 接收信号(这是由Pythonsignal
模块强制执行的 如果底层线程实现支持发送信号 个别线程)。这意味着信号不能用作手段 线程间通信。改为使用锁。
您需要重构您的程序,以便接收信号的主线程不会阻止您保存状态。最简单的方法是使用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")
答案 1 :(得分:1)
始终在主线程中处理信号。当您收到信号时,您不知道它来自哪里。你不能说“在产生信号发送过程的线程中处理它”,因为你不知道信号发送过程是什么。
解决此问题的方法是使用Condition Variables通知所有线程收到信号并且必须关闭它们。
import threading
got_interrupt = False # global variable
def killer_func(cv):
...
with cv:
cv.wait(2)
interupted = got_interrupt # Read got_interrupt while holding the lock
if interrupted:
cleanup()
...
lock = threading.Lock()
notifier_cv = threading.Condition(lock)
killer_thread = threading.Thread(target=killer_func, args=(notifier_cv,))
killer_thread.start()
try:
...
except KeyboardInterrupt:
with cv:
got_interrupt = True
cv.notify_all()