提到多線程,很多人就會望而卻步,本文將由淺入深地帶你攻克python多線程編程,並防止你跳入深坑,
首先看一段簡單的代碼:
1 from time import ctime,sleep 2 def play_video(video): 3 for i in range(2): 4 print "i am playing video: %s at %s"%(video,ctime()) 5 sleep(4) 6 7 8 def play_music(music): 9 for i in range(2): 10 print "i am playing music: %s at %s"%(music,ctime()) 11 sleep(2) 12 13 14 if __name__=="__main__": 15 16 play_video("speed_and_crazy") 17 play_music("chengdu") 18 19 print "all are over at %s"%ctime() 20 21
執行結果:
C:\Python27>python mui_thread.py i am playing video: speed_and_crazy at Mon Jun 26 23:01:59 2017 i am playing video: speed_and_crazy at Mon Jun 26 23:02:03 2017 i am playing music: chengdu at Mon Jun 26 23:02:07 2017 i am playing music: chengdu at Mon Jun 26 23:02:09 2017 all are over at Mon Jun 26 23:02:11 2017
隨着人們對多任務的要求,同時為了充分利用cpu資源,多線程編程不可避免,那么我們如何利用python去實現play_video和play_music
兩個任務同時運行呢?
1 from time import ctime,sleep 2 import threading 3 def play_video(video): 4 for i in range(2): 5 print "i am playing video: %s at %s \n"%(video,ctime()) 6 sleep(5) 7 8 9 def play_music(music): 10 for i in range(2): 11 print "i am playing music: %s at %s \n"%(music,ctime()) 12 sleep(1) 13 14 threads=[] 15 16 thread1=threading.Thread(target=play_video,args=("speed_and_crazy",)) 17 18 threads.append(thread1) 19 20 thread2=threading.Thread(target=play_music,args=("chengdu",)) 21 22 threads.append(thread2) 23 24 25 26 if __name__=="__main__": 27 28 for thread in threads: 29 thread.setDaemon(True) #將線程聲明為守護線程,必須在start()方法調用之前,如果不設置為守護線程,程序會被無限掛起 30 thread.start() 31 32 print "all are over at %s \n"%ctime() 33 34
測試結果:
C:\Python27>python mui_thread.py i am playing video: speed_and_crazy at Mon Jun 26 23:18:52 2017 all are over at Mon Jun 26 23:18:52 2017 i am playing music: chengdu at Mon Jun 26 23:18:52 2017 #從打印的時間可知,play_video、play_music和父進程幾乎同時運行
從結果看,與我們最初的目標相差甚遠,怎么沒有按照順序執行,為什么每個函數都只有一條日記輸出?
那是因為子線程(play_video、play_music)和主線程print "all are over at %s \n"%ctime()都是同一時間啟動,但由於主線程已經運行結束,所以導致子線程也同時終止,在這種條件下,我們如何保證子進程都能夠執行完畢呢?
增加thread.join()並 放在循環外
from time import ctime,sleep import threading def play_video(video): for i in range(2): print "i am playing video: %s at %s \n"%(video,ctime()) sleep(1) def play_music(music): for i in range(2): print "i am playing music: %s at %s \n"%(music,ctime()) sleep(5) threads=[] thread1=threading.Thread(target=play_video,args=("speed_and_crazy",)) threads.append(thread1) thread2=threading.Thread(target=play_music,args=("chengdu",)) threads.append(thread2) if __name__=="__main__": for thread in threads: thread.setDaemon(True) thread.start() thread.join() #加在循環外, print "all are over at %s \n"%ctime()
運行結果:
1 C:\Python27>python mui_thread.py 2 i am playing video: speed_and_crazy at Mon Jun 26 23:32:21 2017 3 i am playing music: chengdu at Mon Jun 26 23:32:21 2017 4 5 6 i am playing video: speed_and_crazy at Mon Jun 26 23:32:22 2017 7 8 i am playing music: chengdu at Mon Jun 26 23:32:26 2017 9 10 all are over at Mon Jun 26 23:32:31 2017
thread.join()的作用是主線程必須等待子線程都執行完了才能結束,play_video、play_music幾乎同時執行
但是如果改變play_video、play_music里面的sleep的時長,即是下面的代碼:
from time import ctime,sleep import threading def play_video(video): for i in range(2): print "i am playing video: %s at %s \n"%(video,ctime()) sleep(5) def play_music(music): for i in range(2): print "i am playing music: %s at %s \n"%(music,ctime()) sleep(1) threads=[] thread1=threading.Thread(target=play_video,args=("speed_and_crazy",)) threads.append(thread1) thread2=threading.Thread(target=play_music,args=("chengdu",)) threads.append(thread2) if __name__=="__main__": for thread in threads: thread.setDaemon(True) thread.start() thread.join() print "all are over at %s \n"%ctime()
此時運行結果:
C:\Python27>python mui_thread.py i am playing video: speed_and_crazy at Mon Jun 26 23:44:13 2017 i am playing music: chengdu at Mon Jun 26 23:44:13 2017 i am playing music: chengdu at Mon Jun 26 23:44:14 2017 all are over at Mon Jun 26 23:44:15 2017
我們看到play_video還有一條log沒有打印出來,原因是thread.join()在循環外,此時的thread為play_music,父進程只會等待play_music進程執行完就結束,而不會等待play_video(sleep時間較長)執行完才結束,所以才會有上面的結果