之前文章讨论了进程意外退出时,如何将主进程创建的子进程终止,避免形成孤儿进程,两种做法,一种是将是将主进程中创建的子进程信息保存,使用信号处理机制,在主进程收到终止信号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)