我有以下python3程序,它创建了许多工作进程并在我按下ctrl-c时杀死它们。
在将SIGTERM发送到子进程之前,主服务器在控制台print('[W] aghhh ... %d' % self.pid)
上打印一行。问题是对于给定的过程,该行输出不止一次。像这样(实际的控制台输出):
[W] aghhh ... 15773
[W] aghhh ... 15773
[W] aghhh ... 15774
[W] aghhh ... 15773
[W] aghhh ... 15774
[W] aghhh ... 15775
[W] aghhh ... 15776
问题:这怎么可能? 什么是杀死子进程的正确方法?
代码:
import os
import time
import signal
import sys
class ChildProcess:
def __init__(self, m):
self.pid = None
self.ttl = 10
self.master = m
self.pipe_in = None
self.pipe_out = None
def hey(self):
self.ttl -= 1
self.pipe_out.write('Hello worker %d\n' % self.pid)
self.pipe_out.flush()
def tell_me(self):
msg = self.pipe_in.readline()
print('[M] Worker process %d says: %s' % (self.pid, msg), end='')
def live(self):
r1, w1 = os.pipe()
r2, w2 = os.pipe()
pid = os.fork()
self.pid = pid
if pid:
print('[M] Created worker process %d' % pid)
os.close(w1)
os.close(r2)
self.pipe_in = os.fdopen(r1, 'rt')
self.pipe_out = os.fdopen(w2, 'wt')
self.master.add(self)
else:
print('[W] Worker process ready to rock')
os.close(r1)
os.close(w2)
wr = os.fdopen(w1, 'wt')
reader = os.fdopen(r2)
while True:
wr.write('Hello Master\n')
wr.flush()
msg = reader.readline()
print('[W] Master says %s' % msg, end='')
def die(self):
print('[W] aghhh ... %d' % self.pid)
os.kill(self.pid, signal.SIGTERM)
class Master:
def __init__(self):
self.workers = []
def add(self, worker):
self.workers.append(worker)
def rulez(self, nbr=2):
for i in range(nbr):
worker = ChildProcess(self)
worker.live()
while True:
for w in self.workers:
w.tell_me()
time.sleep(1)
w.hey()
def reap(self):
for w in self.workers:
w.die()
if __name__ == '__main__':
master = Master()
try:
master.rulez(3)
except KeyboardInterrupt:
master.reap()
答案 0 :(得分:2)
这是因为子进程正在执行与父进程相同的代码。所以对于子进程,当他们执行while True: wr.write('Hello Master\n') …
时,如果他们在被父进程杀死之前收到SIG_INT,他们会将KeyboardInterrupt
提升到调用方法,即master.rulez(3)
}。
所以,是的,实际上KeyboardInterrupt
中最多会有4 master.rulez(3)
次提升。您可以通过在except KeyboardInterrupt
打印内容来确认,或者更好,打印len(self.workers)
。这会产生这样的东西:
... [W] Master says Hello worker 769 ^C3 2 1 [W] aghhh ... 769 0 [W] aghhh ... 769 [W] aghhh ... 769 [W] aghhh ... 770 [W] aghhh ... 770 [W] aghhh ... 771
请注意,当主人可能分叉其他孩子时,每个孩子都会分叉,因此“self.workers”中还有其他一些孩子。因此,对于第一个孩子,这将是空的,第二个孩子将是1,第三个孩子将是2。
您的代码可视化(针对两名员工):
Master | | ChildProcess1 (init) | +---------------------+ (fork) | | self.workers.add(1) While True: ... | | | (KeyboardInterrupt) | master.reap() ChildProcess2 (init) (exit) | +---------------------+ (fork) <---- This copied self.workers also, which | | already contains ChildProcess1 self.workers.add(2) While True: ... while True: ... | (KeyboardInterrupt) (KeyboardInterrupt) master.reap() master.reap() ChildProcess1.die() ChildProcess1.die() ChildProcess2.die() (exit) (exit)
为了防止孩子继续执行master.rulez(3)
,您可以在子进程中捕获KeyboardInterrupt
,然后在那里引发sys.exit()
(或者它可以使用{{1另外)
代码:
os.kill()
结果:
... [W] Master says Hello worker 779 ^C3 [W] aghhh ... 779 [W] aghhh ... 780 [W] aghhh ... 781