進程與線程
1.進程:計算機程序只是存儲在磁盤中的可執行二進制(或其他類型)的文件。只有把他們加載到內存中並被操作系統調用,才具有其生命周期。進程則是一個執行中的程序。每個進程都擁有自己的地址空間,內存,數據棧以及其他用於跟蹤執行的輔助數據。進程也可以通過派生新的進程來執行其他任務。由於每個進程有自己的數據,所以只能采用進程間通信(IPC)的方式來共享信息。
2.線程:又稱輕量級進程。一個進程開始便會創建一個線程,稱為主線程。一個進程可以創建多個線程,多線程即是同一進程下的不同執行路徑,同一進程下的線程共享該進程的數據區。線程以並發的方式執行,線程執行時可以被中斷和掛起。(在多核cpu中,多線程才可能並行執行)
圖解多線程與單線程:單核與多核
線程和python
1.全局解釋器鎖
python代碼執行是由python虛擬機(又稱解釋器主循環)控制的。python在主循環中同時只能有一個控制線程在執行,就像單核cpu系統中,內存中可以有很多程序,但任意給定時刻只能有一個程序在運行。同理,盡管python解釋器可以運行多線程,但在任意給定時刻只有一個線程會被解釋器執行。
對python虛擬機由全局解釋器鎖(GIL)控制。正是由於GIL的存在,python解釋器在某一時刻只能讓一個線程執行。多線程執行方式如下:
1)設置GIL
2)切換一個線程去執行
3)執行下面操作之一
a.指定數量字節碼指令
b.線程讓出控制權
4)線程設置成睡眠(掛起)狀態
5)解鎖GIL
6)重復1-5
2.python多線程的作用,原理,缺陷
作用:提高程序執行速度。
原理:多線程能夠提高執行速度的原因是什么?假如一個程序包含多個子任務,這些任務相互獨立,沒有因果關系。
a.單線程情況下,執行過程中,某個子任務在等待I/O,然而I/O到來的時間不確定,cpu時間耗在毫無意義的等待上,程序執行時間也將加上這一段等待的時間。
b.多線程情況下,若某個子任務等待I/O,可切換出其他線程執行,等到合適的時機(I/O到達)再切換回該線程,避免了cpu無意義的等待,也降低了程序的執行時間。
缺陷:由於GIL的存在,python多線程中只能有一個線程會被執行,因而無法利用多核cpu能夠實現並行執行的特點。不僅如此,由於線程的切換需要時間開銷,多線程使用不當的程序執行速度還可能要慢於單線程程序執行的速度。
3.python多線程的使用場合
I/O密集型應用
_thread模塊
python的_thread模塊提供了基本的線程和互斥鎖支持,threading模塊則提供了功能更全面的線程管理。以下討論_thread模塊
主要方法
_thread.start_new_thread(function,args,kwargs=None) //派生一個新的線程,給定agrs和kwargs來執行function
_thread.allocate_lock() //分配鎖對象
_thread.exit() //線程退出
lock.acquire(waitflag=1, timeout=-1) //獲取鎖對象
lock.locked() //如果獲取了鎖對象返回true,否則返回false
lock.release() //釋放鎖
其他方法
_thread.LockType() //鎖對象類型
_thread.get_ident() //獲取線程標識符
-thread.TIMEOUT_MAX //lock.acquire的最大時間,超時將引發OverflowError
_thread.interrupt_main() //引發主線程KeyboardInterrupt錯誤,子線程可以用這個函數來終止主線程
簡單實例
4個線程分別執行loop函數,中間等待nsec秒,nsec分別為4,2,3,5
1 #!/usr/bin/env python3 2 # coding:utf-8 3 from time import ctime 4 from time import sleep 5 import _thread 6 7 loops = [4, 2, 3, 5] 8 9 10 def loop(nloop, nsec, lock): # 參數依次為:標識,睡眠時間,鎖對象 11 print("start loop", nloop, 'at:', ctime()) 12 sleep(nsec) 13 print("loop", nloop, "done at:", ctime()) 14 lock.release() # 釋放鎖 15 16 17 def main(): 18 print('start at:', ctime()) 19 locks = [] 20 nloops = range(len(loops)) 21 22 for i in nloops: 23 lock = _thread.allocate_lock() # 分配鎖對象 24 lock.acquire() # 獲取鎖對象 25 locks.append(lock) 26 27 for i in nloops: 28 _thread.start_new(loop, (i, loops[i], locks[i])) //派生新線程 29 30 # 等待所有鎖被釋放 31 for i in nloops: 32 while(locks[i].locked()): 33 pass 34 print('all DONE at', ctime()) 35 36 37 if __name__ == '__main__': 38 main()
執行結果:
start at: Mon Jan 22 16:09:10 2018
start loop 2 at: Mon Jan 22 16:09:10 2018
start loop 1 at: Mon Jan 22 16:09:10 2018
start loop 3 at: Mon Jan 22 16:09:10 2018
start loop 0 at: Mon Jan 22 16:09:10 2018
loop 1 done at: Mon Jan 22 16:09:12 2018
loop 2 done at: Mon Jan 22 16:09:13 2018
loop 0 done at: Mon Jan 22 16:09:14 2018
loop 3 done at: Mon Jan 22 16:09:15 2018
all DONE at Mon Jan 22 16:09:15 2018
分別等待4,2,3,5秒,程序運行總時間5秒。main()函數最后一個循環作用是等待所有子線程退出。
注意事項
_thread對於進程何時退出沒有任何控制。當主線程結束時,所有其他線程也都強制結束。不會發出警告或者進行適當的清理。因而python多線程一般使用較為高級的threading模塊,它提供了完整的線程控制機制以及信號量機制。
可參考: