項目中經常需要用到多線程,如果一個python程序用了多線程,當子線程沒有結束時,用ctrl+c是關閉不了主線程的,這時候就只能用kill命令殺掉,這樣會很麻煩。
所以探討了下怎么ctrl+C關閉多線程python程序,也在網上查了很多別人的做法,自己做了很多實驗,嘗試了很多種方法,總結得出一個能用的方法就是,把子線程setDeamon(True),通過isAlive方法實現join的功能。
代碼:
#encoding=utf-8 __author__ = 'kevinlu1010@qq.com' import threading from time import sleep def f(): sleep(100) p=threading.Thread(target=f) p.setDaemon(True) p.start() # p.join() while 1: if not p.isAlive(): break sleep(1) print 'done'
當子線程很多的時候,可以用這個函數
def threads_join(threads): ''' 令主線程阻塞,等待子線程執行完才繼續,使用這個方法比使用join的好處是,可以ctrl+c kill掉進程 ''' for t in threads: while 1: if t.isAlive(): sleep(10) else: break
這種做法的壞處就是令主線程阻塞,直到子線程執行完這個功能的實現太麻煩了,原本用join來實現就好方便很多
下面是研究的過程中的嘗試,但是全部都實現不了ctrl+C關閉的功能
原始的多線程程序
def f(): sleep(100) p=threading.Thread(target=f) p.start() p.join() print 'done'
這是最原始的一個多線程程序。
嘗試一:設置線程為守護線程,即加入
p.setDaemon(True)
但是ctrl+c,程序沒反應,跟沒加是一樣的
嘗試二:使用信號,因為ctrl c的時候系統會向程序發送sigint信號,所以我們可以令程序捕獲這個信號,並調用os的kill方法殺死自己
import signal import os import threading from time import sleep def f(a,b): print 'kill me' os.kill(os.getpid(),signal.SIGKILL) def tf(): sleep(20) signal.signal(signal.SIGINT,f) p=threading.Thread(target=tf) p.start() p.join() print 'done'
程序運行后,我立刻按ctrl c ,主線程會等子線程sleep20后,才會print 'kill me',證明主線程在等待子線程執行的時候,即join的時候,是捕獲不了系統發來的信號的,要等子線程執行完畢,才能捕獲。所以這個方法還是不行。
嘗試三,用一個標志來讓子線程自己結束自己的運行
is_exit=0 def f(a,b): global is_exit is_exit=1 print 'kill me' os.kill(os.getpid(),signal.SIGKILL) def tf(): while not is_exit: sleep(20) signal.signal(signal.SIGINT,f) p=threading.Thread(target=tf) p.start() while 1: sleep(10) print 'done'
這里加入一個標志is_exit用來標志子線程是否繼續執行,然后加入信號,當捕獲關閉信號時,把is_exit改為1,令到子線程自己結束,由於主線程在join的狀態下是接受不了信號的,所以這里讓主線程處於一直等待的狀態。
這個做法是能做到ctrl c關閉子線程的,缺點就是子線程需要做完一個循環才能結束,同時主線程沒有了join的功能,適用於主線程在給子線程發放任務后就不需要做任何操作的情形。
所以總的來說,ctrl c不能關閉多線程的程序的主要原因是使用了join方法,一旦用了join,主線程就會一直處於阻塞狀態,不接受任何外界的聯系。但是join方法在實際的業務中是經常需要用到的,我查了很久也沒有查到可以替代join的,同時可以被ctrl c的方法。上面第一個程序用到的使用alive方法來實現join的功能的做法算是一個不太好,但又不能不使用它的解決方案了,希望后面能找到更好的實現join功能的方法。