Python
中的線程是操作系統(tǒng)的原生線程,Python
虛擬機內(nèi)部使用一個全局解釋器鎖(Global Interpreter Lock
,GIL
)來互斥線程對Python
虛擬機的使用。
0x01 GIL與線程調(diào)度
為了支持多線程,一個基本的要求就是需要實現(xiàn)不同線程對共享資源訪問的互斥,這正是引入
GIL
的根本原因刺覆。
Python
中的GIL
是一個非常霸道的互斥實現(xiàn):在一個線程擁有了解釋器(獲得了GIL
)的訪問權(quán)限后严肪,其他所有的線程都必須等待它釋放解釋器的訪問權(quán)限,即使這些線程的下一條指令并不會互相影響谦屑。
初看上去驳糯,感覺這樣的機制粒度太大了。我們似乎只需要將可能被多個線程訪問的資源保護起來即可氢橙,對于不會被多個線程訪問的資源酝枢,完全可以不用保護。
在
Python
發(fā)展歷史中悍手,這樣的方案出現(xiàn)過帘睦,但是經(jīng)過測試證實,效率沒有GIL
好坦康。
GIL
的機制竣付,導(dǎo)致多處理器的情形退化為單處理器(多處理器可以同時執(zhí)行多個線程),性能大打折扣滞欠。
對于線程調(diào)度機制而言古胆,同操作系統(tǒng)的進程調(diào)度一樣,最關(guān)鍵的是要解決兩個問題:
- 在何時掛起當(dāng)前線程筛璧,選擇處于等待狀態(tài)的下一個線程逸绎?
- 在眾多處于等待狀態(tài)的線程中,選擇激活哪一個線程夭谤?
何時進行線程調(diào)度呢棺牧?
這個是由
Python
自身決定的。操作系統(tǒng)是怎樣進行進程的切換的:當(dāng)一個進程執(zhí)行了一段時間后朗儒,發(fā)生了時鐘中斷陨帆,操作系統(tǒng)響應(yīng)時鐘中斷曲秉,并在這時開始進行進程的調(diào)度。同樣疲牵,
Python
也是通過軟件模擬了時鐘中斷承二,來激活線程的調(diào)度。
Python
字節(jié)碼解釋器的工作原理是按照指令的順序一條一條的順序執(zhí)行纲爸,Python
內(nèi)部維護著一個數(shù)值N
(這個數(shù)值N就是Python
內(nèi)部的時鐘)亥鸠,意味著Python
在執(zhí)行了N
條指令以后應(yīng)該立即啟動線程調(diào)度機制。可以通過
sys.getcheckinterval()
來獲取這個值识啦,也可以通過sys.setcheckinterval()
來更改這個值负蚊。在
Python
內(nèi)部也使用這個值來檢查是否有異步的事件(event
)發(fā)生,需要處理颓哮。
選擇哪個等待線程來執(zhí)行家妆?
不知道。
這個選擇機制
Python
交給了底層操作系統(tǒng)來解決冕茅。也就是說伤极,
Python
借助了底層操作系統(tǒng)所提供的線程調(diào)度機制來選擇下一個使用Python
解釋器的線程是哪一個。
上面的解釋就意味著姨伤,Python
中的線程實際上就是操作系統(tǒng)所支持的原生線程哨坪,而不是模擬出來的。
Python
的多線程機制正是建立在操作系統(tǒng)的原生線程的基礎(chǔ)之上乍楚,對應(yīng)不同的操作系統(tǒng)有不同的實現(xiàn)当编。然而最終,在各不相同的原生線程的基礎(chǔ)之上徒溪,Python
提供了一套統(tǒng)一的抽象機制忿偷,給Python
的使用者一個簡單方便的多線程工具箱(thread
、threading
)臊泌。