我有以下 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()
最佳答案
这是因为子进程正在执行与父进程相同的代码。因此,对于子进程,当他们执行 while True: wr.write('Hello Master\n') …
时,如果它们在被父进程杀死之前收到 SIG_INT,它们将引发 KeyboardInterrupt
到调用方法,即 master.rulez(3)
.
所以,是的,实际上最多有 4 KeyboardInterrupt
提出于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
Note that each child is forked when the master had probably forked other children, and so there are some other children in the 'self.workers'. So for the first child, this will be empty, the second child this would be 1, the third child this would be 2.
Visualisation of your code (for two workers):
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)
To prevent the child from continuing execution of master.rulez(3)
, you can catch the KeyboardInterrupt
in the child process, and then raise sys.exit()
there (or it can kill itself using os.kill()
also)
Code:
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),
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)
try:
while True:
wr.write('Hello Master\n')
wr.flush()
msg = reader.readline()
print('[W] Master says %s' % msg),
except KeyboardInterrupt:
sys.exit()
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):
print len(self.workers)
for w in self.workers:
w.die()
if __name__ == '__main__':
master = Master()
try:
master.rulez(4)
except KeyboardInterrupt:
master.reap()
结果:
... [W] Master says Hello worker 779 ^C3 [W] aghhh ... 779 [W] aghhh ... 780 [W] aghhh ... 781
关于Python:进程被杀死后多次打印到控制台,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20622889/