一.關於Python多線程
Python解釋器中可以同時運行多個線程,但是再任意時刻只能有一個線程在解釋器運行。
Python虛擬機的訪問是由全局解鎖器(GIL)控制的,由GIL保證同時只有一個線程的運行。
執行方式如下:
1.設置GIL
2.切換進一個進程執行
3.執行下面操作中的一個
a.運行指定數量的字節碼(操作系統中是由時鍾控制的)
b.線程主動出讓控制權
4.把線程設置為睡眠狀態,即切換出線程
5.解鎖GIL
6.重復以上步驟
注意:1.調用外部代碼時(C/C++擴展的內置函數),GIL保持鎖定,因為外部代碼沒有Python字節碼.
2.I/O密集型的Python程序要比計算密集型的程序更好的利用多線程。
二.關於thead模塊的多線程:
示例1,當沒有使用多線程時:
1 from time import sleep, ctime 2 3 def loop(): 4 print('start loop0 at:', ctime()) 5 sleep(4) 6 print('loop 0 done at:', ctime()) 7 8 def loop1(): 9 print('start loop1 at:', ctime()) 10 sleep(2) 11 print('loop1 done at:', ctime()) 12 13 def main(): 14 print('starting at:', ctime()) 15 loop() 16 loop1() 17 print('loop1 done at:', ctime()) 18 19 if __name__ == '__main__': 20 main()
輸出結果為:
starting at: Mon Dec 19 22:17:42 2016
start loop0 at: Mon Dec 19 22:17:42 2016
loop 0 done at: Mon Dec 19 22:17:46 2016
start loop1 at: Mon Dec 19 22:17:46 2016
loop1 done at: Mon Dec 19 22:17:48 2016
loop1 done at: Mon Dec 19 22:17:48 2016
從結果可以看出loop0和loop按照順序執行,耗時一共6s
thread模塊的核心函數是:start_new_thread()
用法:_thread.start_new_thread(func, *arg, **kwargs)
示例2,當使用多線程時候:
1 import _thread 2 from time import sleep, ctime 3 4 def loop0(): 5 print('start loop0 at:', ctime()) 6 sleep(4) 7 print('loop0 done at:', ctime()) 8 9 def loop1(): 10 print('start loop1 at:', ctime()) 11 sleep(2) 12 print('loop1 done at:', ctime()) 13 14 def main(): 15 print('starting at:', ctime()) 16 _thread.start_new_thread(loop0, ()) 17 _thread.start_new_thread(loop1, ()) 18 sleep(6) 19 print('all done at:', ctime()) 20 21 22 if __name__ == '__main__': 23 main()
結果為:
starting at: Mon Dec 19 22:27:57 2016
start loop0 at: Mon Dec 19 22:27:57 2016
start loop1 at: Mon Dec 19 22:27:57 2016
loop1 done at: Mon Dec 19 22:27:59 2016
loop0 done at: Mon Dec 19 22:28:01 2016
all done at: Mon Dec 19 22:28:03 2016
從結果可以看出,1.結果不是按照順序輸出的,loop0后結束
2.loop0和loop1的運行時間一共是4s,比沒有使用多線程快了2s
3.sleep(6)是針對主線程的,預計loop0和loop1會在6s前執行完畢。
如果我們把主線程的執行時間設定為3s,那么結果就有意思了
starting at: Mon Dec 19 22:33:01 2016
start loop0 at: Mon Dec 19 22:33:01 2016
start loop1 at: Mon Dec 19 22:33:01 2016
loop1 done at: Mon Dec 19 22:33:03 2016
all done at: Mon Dec 19 22:33:04 2016
從這個結果哦可以看出,loop0沒有執行完畢就被迫退出!!!原因是主線程的切出時間為3s,3s
過后主線程繼續執行,loop0即使沒運行完畢,被迫退出!!
從這里也可以看出,對主線程執行sleep()來達到同步的目的不可靠,所以引入鎖的概念。
示例3,使用鎖的多線程:
1 import _thread 2 from time import sleep, ctime 3 4 loops = [4, 2] 5 6 7 def loop(nloop, nsec, lock): 8 print('start loop', nloop, 'at:', ctime()) 9 sleep(nsec) 10 print('loop', nloop, 'done at', ctime()) 11 lock.release() #釋放鎖 12 13 14 def main(): 15 print('starting at:', ctime()) 16 locks = [] 17 18 for i in range(len(loops)): 19 lock = _thread.allocate_lock() 20 lock.acquire() #鎖上鎖 21 locks.append(lock) #保存鎖 22 23 for i in range(len(loops)): 24 _thread.start_new_thread(loop, (i, loops[i], locks[i])) 25 26 for i in range(len(loops)): 27 while locks[i].locked(): pass #使用忙等待暫停主線程,直到所有的鎖都被釋放繼續主線程, 28 29 print('all done at', ctime()) 30 31 if __name__ == '__main__': 32 main()
結果:
starting at: Mon Dec 19 22:52:02 2016
start loop 0 at: Mon Dec 19 22:52:02 2016
start loop 1 at: Mon Dec 19 22:52:02 2016
loop 1 done at Mon Dec 19 22:52:04 2016
loop 0 done at Mon Dec 19 22:52:06 2016
all done at Mon Dec 19 22:52:06 2016
從結果可以看出,使用所的多線程,運行時間為4s,而且不用人為的設置主線程切出時間。
參考資料:Python核心編程.第四章.Wesley Chun著