之前文章討論了進程意外退出時,如何將主進程創建的子進程終止,避免形成孤兒進程,兩種做法,一種是將是將主進程中創建的子進程信息保存,使用信號處理機制,在主進程收到終止信號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)