花了一天時間用python爲服務寫了個壓力測試。很簡單,多線程向服務器發請求。但寫完以後發現若是中途想停下來,按Ctrl+C達不到效果,天然想到要用信號處理函數捕捉信號,使線程都停下來,問題解決的方法請往下看: python
#!/bin/env python # -*- coding: utf-8 -*- #filename: peartest.py import threading, signal is_exit = False def doStress(i, cc): global is_exit idx = i while not is_exit: if (idx < 10000000): print "thread[%d]: idx=%d"%(i, idx) idx = idx + cc else: break print "thread[%d] complete."%i def handler(signum, frame): global is_exit is_exit = True print "receive a signal %d, is_exit = %d"%(signum, is_exit) if __name__ == "__main__": signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) cc = 5 for i in range(cc): t = threading.Thread(target=doStress, args=(i,cc)) t.start()
上面是一個模擬程序,並不真正向服務發送請求,而代之以在一千萬之內,每一個線程每隔併發數個(cc個)打印一個整數。很明顯,當全部線程都完成本身的任務後,進程會正常退出。但若是咱們中途想退出(試想一個壓力測試程序,在中途已經發現了問題,須要中止測試),該腫麼辦?你固然能夠用ps查找到進程號,而後kill -9殺掉,但這樣太繁瑣了,捕捉Ctrl+C是最天然的想法。上面示例程序中已經捕捉了這個信號,並修改全局變量is_exit,線程中會檢測這個變量,及時退出。 服務器
但事實上這個程序並不work,當你按下Ctrl+C時,程序照常運行,並沒有任何響應。網上搜了一些資料,明白是python的子線程若是不是daemon的話,主線程是不能響應任何中斷的。但設爲daemon後主線程會隨之退出,接着整個進程很快就退出了,因此還須要在主線程中檢測各個子線程的狀態,直到全部子線程退出後本身才退出,所以上例29行以後的代碼能夠修改成: 多線程
threads=[] for i in range(cc): t = threading.Thread(target=doStress, args=(i, cc)) t.setDaemon(True) threads.append(t) t.start() for i in range(cc): threads[i].join()
從新試一下,問題依然沒有解決,進程仍是沒有響應Ctrl+C,這是由於join()函數一樣會waiting在一個鎖上,使主線程沒法捕獲信號。所以繼續修改,調用線程的isAlive()函數判斷線程是否完成: 併發
while 1: alive = False for i in range(cc): alive = alive or threads[i].isAlive() if not alive: break
這樣修改後,程序徹底按照預想運行了:能夠順利的打印每一個線程應該打印的全部數字,也能夠中途用Ctrl+C終結整個進程。完整的代碼以下: app
#!/bin/env python # -*- coding: utf-8 -*- #filename: peartest.py import threading, signal is_exit = False def doStress(i, cc): global is_exit idx = i while not is_exit: if (idx < 10000000): print "thread[%d]: idx=%d"%(i, idx) idx = idx + cc else: break if is_exit: print "receive a signal to exit, thread[%d] stop."%i else: print "thread[%d] complete."%i def handler(signum, frame): global is_exit is_exit = True print "receive a signal %d, is_exit = %d"%(signum, is_exit) if __name__ == "__main__": signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) cc = 5 threads = [] for i in range(cc): t = threading.Thread(target=doStress, args=(i,cc)) t.setDaemon(True) threads.append(t) t.start() while 1: alive = False for i in range(cc): alive = alive or threads[i].isAlive() if not alive: break
其實,若是用python寫一個服務,也須要這樣,由於負責服務的那個線程是永遠在那裏接收請求的,不會退出,而若是你想用Ctrl+C殺死整個服務,跟上面的壓力測試程序是一個道理。 函數
總結一下,python多線程中要響應Ctrl+C的信號以殺死整個進程,須要: 測試
1.把全部子線程設爲Daemon;
2.使用isAlive()函數判斷全部子線程是否完成,而不是在主線程中用join()函數等待完成;
3.寫一個響應Ctrl+C信號的函數,修改全局變量,使得各子線程可以檢測到,並正常退出。 線程