我们有一个 Python 应用程序,在其运行期间会创建一些线程,然后创建一个新的进程和自己一起运行,最后退出。
from pprint import pprint
import os
import random
import signal
import sys
import threading
import time
class Name(object):
def __init__(self, name):
self.name = name
class CallThreads(threading.Thread):
def __init__(self, target, *args):
self.target = target
self.args = args
threading.Thread.__init__(self)
def run (self):
self.target(*self.args)
def main(args):
print("Hello, world!")
letter = random.choice(['A', 'B', 'C', 'D', 'E', 'F'])
count = 0
while count<3:
count += 1
name = Name(letter+str(count))
t = CallThreads(provider_query, name)
t.daemon = True
t.start()
time.sleep(3)
print("------------")
print("Time to die!")
os.system('python restart.py')
sys.exit(0)
def provider_query(name):
while name.name!='':
print(name.name)
time.sleep(1)
def signal_handler(signal, frame):
sys.exit()
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main(sys.argv)
我们期望在进程退出后,与它关联的线程将停止运行。然而,在 Kubuntu Linux 上运行上述程序后发现,即使应用程序已经退出并生成了一个新进程,这些进程仍然继续运行。我们可以看到这些线程仍然在向标准输出打印信息。即使将守护进程行注释掉,这些进程仍然继续运行。
为什么在调用 sys.exit() 之后,这些进程仍然继续运行?如何让它们停止?
2、解决方案
问题在于我们永远无法到达 sys.exit() 调用。os.system(‘python restart.py’) 会阻塞,直到 restart.py 完成。但这个子进程 restart.py 在另一个子进程 restart.py 上阻塞,以此类推,直到进程表最终爆掉。在进程表中大约每 9 秒就会看到另一个 restart.py。
为了解决这个问题,我们需要使用 fork() 创建一个子进程来重启应用程序,而不是使用 os.system(‘python restart.py’)。这将允许父进程在子进程启动后立即退出,并且子进程将继续运行应用程序。
修改后的代码如下:
from pprint import pprint
import os
import random
import signal
import sys
import threading
import time
class Name(object):
def __init__(self, name):
self.name = name
class CallThreads(threading.Thread):
def __init__(self, target, *args):
self.target = target
self.args = args
threading.Thread.__init__(self)
def run (self):
self.target(*self.args)
def main(args):
print("Hello, world!")
letter = random.choice(['A', 'B', 'C', 'D', 'E', 'F'])
count = 0
while count<3:
count += 1
name = Name(letter+str(count))
t = CallThreads(provider_query, name)
t.daemon = True
t.start()
time.sleep(3)
print("------------")
print("Time to die!")
# 使用 fork() 创建子进程来重启应用程序
pid = os.fork()
if pid == 0:
# 子进程重新运行应用程序
os.execvp('python', ['python', 'restart.py'])
else:
# 父进程退出
sys.exit(0)
def provider_query(name):
while name.name!='':
print(name.name)
time.sleep(1)
def signal_handler(signal, frame):
sys.exit()
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main(sys.argv)