主进程被杀死时,如何保证子进程同时退出,而不变为孤儿进程(二)

时间:2021-08-03 23:48:09

  之前文章讨论了进程意外退出时,如何将主进程创建的子进程终止,避免形成孤儿进程,两种做法,一种是将是将主进程中创建的子进程信息保存,使用信号处理机制,在主进程收到终止信号SIGTERM时,保存的子进程信息terminate,之后主进程退出;另一种是更加直接,通过进程组id将整个进程组中的进程杀死。这两种方式主要的差别是,第一种方式是通过向主进程发送SIGTERM请求来杀死主进程和其子进程的,而第二种方式主进程和其子进程都可以收到SIGTERM,将进程组中的所有进程杀死。

  这节我们继续研究下上节第一种方法示例中的代码。代码中用一个全局的processes变量保存子进程信息,这是因为标准库中signal.signal方法接收的回调函数action,包含两个参数,拥有各自的含义,所以没有办法,将子进程信息通过参数传递给action回调函数:

signal(sig, action) -> action
    Set the action for the given signal.  The action can be SIG_DFL, SIG_IGN, or a callable Python object.  The previous action is returned.  
    See getsignal() for possible return values.
    
    *** IMPORTANT NOTICE ***
    A signal handler function is called with two arguments:
    the first is the signal number, the second is the interrupted stack frame.

  那么有没有更优雅的做法,能够将processes通过函数调用,传递给回调函数,避免使用全局变量呢?答案是肯定得。python标准库functools向我们提供了partial偏函数,它的用途是让一些参数在函数被调用之前提前获知其值,位置参数和关键字参数均可应用,我们来看个例子:

from functools import partial
def add(a, b):
    return a + b

add_with_hundred = partial(add, 100)
result = add_with_hundred(10)
print result
110

    代码示例中,partial(add, 100)返回一个partial对象,参数add表示要封装的方法,参数100表示位置参数,它表示的位置是add方法中第一个参数,相当于对add方法的第一个参数添加了默认值100,对于返回的add_with_hundred对象,它的第一个参数默认已经是100,那么在使用时只需要传入一个参数即可。再来看一个关键字参数的例子:

from functools import partial
basetwo = partial(int, base=2)
result = basetwo('101')
print result
5

     int方法用于将字符串类型的变量转换为整型,第二个参数base表示以base进制进行转换。代码中,partial(int, base=2)指定了返回的对象basetwo,以2进制进行转换。通过这两个例子,我们了解了partial方法的用途,我们就可以对上节的代码进行简单的修改,来避免出现全局变量的使用:  

 1 def term(t_processes, sig_num, frame):
 2     print 'terminate process %d' % os.getpid()
 3     try:
 4         print 'the processes is %s' % processes
 5         for p in processes:
 6             print 'process %d terminate' % p.pid
 7             p.terminate()
 8     except Exception as e:
 9         print str(e)
10 
11 
12 if __name__ == '__main__':
13     print 'current main-process pid is %s' % os.getpid()
14     processes = [] 
15     for i in range(3):
16         t = Process(target=fun, args=(str(i),))
17         t.daemon = True
18         t.start()
19         processes.append(t)
20     
21     # handler使用partital处理,用local processes对term方法的第一个参数进行绑定
22     handler = functools.partial(term, processes)
23     signal.signal(signal.SIGTERM, handler)
24     try:
25         for p in processes:
26             p.join()
27     except Exception as e:
28         print str(e)