2013-12-16 101 views
2

我有下面的python3程序,它創建一些工作進程並在我點擊ctrl-c時殺死它們。 在將SIGTERM發送到子進程之前,主控制檯在控制檯print('[W] aghhh ... %d' % self.pid)上打印一行。問題在於,對於給定的進程,該行會輸出多次。像這樣(實際控制檯輸出):Python:在進程打印後多次打印到控制檯

[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() 
+1

您可能需要使用標誌的工作線程while循環,然後設置標誌設置爲false,當你想殺死工人。我的兩分錢.. – Jodgod

+1

與你的印刷問題無關:你可以創建一個新的會話(在分岔孩子之前分叉主和呼叫setsid),然後你可以通過向流程組發送一個單一的信號來殺死他們:'os.killpg( master_pid,SIGKILL)' – jfs

+1

您是否嘗試過使用未緩衝的標準流運行腳本 - '-u'標誌(或者在fork之前手動調用'sys.stdout.flush()''')? – jfs

回答

2

這是因爲一個事實,即子進程正在執行相同的代碼如父。因此,對於子進程,當他們在執行while True: wr.write('Hello Master\n') …時,如果他們在被父進程殺死之前收到SIG_INT,他們會將KeyboardInterrupt提升爲調用方法,即master.rulez(3)

所以,是的,實際上在master.rulez(3)中最多會有4 KeyboardInterrupt。您可以通過在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()有(或者可以使用os.kill()也殺死自己)

代碼:

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 
+0

你是對的這是每個孩子都得到一份'self.workers'的事實。 'die'被主人和隨後創建的子進程調用。對於4個子進程,'死'方法對於子1被調用4次,對於子2被調用3次,等等。謝謝。 – Imaxd