Python:进程被杀死后多次打印到控制台

标签 python fork

我有以下 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/

相关文章:

C Pthreads - 父/子

c - 是否可以 fork/exec 并保证一个先于另一个启动?

python - 如何在 Keras 模型中停用使用training=True 调用的 dropout 层?

python - Ansible 需要 python-apt 但它已经安装

c - 做 fork 时有趣的 parent 和 child 的行为

linux - fork() 如何为子进程返回

python - 将 __slots__ 与 SQLAlchemy 模型一起使用

python - 如何在Python中解压结构体?

python - 从 python 混合器接收空值

fork - 当多个 block 同时添加到区 block 链时会发生什么?