Python的代碼執行由 Python虛擬機(也叫解釋器主循環,CPython版本)來控制,Python在設計之初就考慮到在解釋器的主循環中,同時只有一個線程在運行。即每一個CPU在任意時刻只有一個線程在解釋器中運行。對 Python虛擬機訪問的控制由全局解釋鎖GIL控制,正是這個鎖來控制同一時刻只有一個線程可以運行。——在單核CPU下的多線程其實都只是併發,不是並行 。python
併發與並行區別網絡
併發和並行的意義多線程
併發和並行均可以處理「多任務」,兩者的主要區別在因而否是「同時進行」多個的任務。可是涉及到任務分解(有前後依賴耦合度高的任務沒法作到並行)、任務運行(可能要考慮互斥、鎖、共享等)、結果合併。併發
在Python多線程下,每一個線程的執行方式,以下:函數
在Python2中,在解釋器解釋執行任何 Python 代碼時,都須要先得到這把鎖才行(同一時間只會有一個得到了 GIL 的線程在跑,其它的線程都處於等待狀態等着 GIL 的釋放),在遇到 I/O 操做時會釋放這把鎖。若是是純計算的程序,沒有 I/O 操做,解釋器會每隔 100 次操做就釋放這把鎖,讓別的線程有機會執行(這個次數能夠經過 sys.setcheckinterval 來調整)也正是這種設定,是的多線程的CPU密集型計算很是雞肋,下面會講到爲什麼如此。性能
而在python3中,GIL不使用ticks計數(100次,釋放GIL),改成使用計時器(執行時間達到15ms閾值後,當前線程釋放 GIL),使得執行計算的次數更多,釋放次數減小,這樣對CPU密集型程序更加友好,但依然沒有解決GIL致使的同一時間只能執行一個線程的問題,因此效率依然不盡如人意。測試
CPU密集型(各類循環處理、計數等等),在這種狀況下,ticks計數很快就會達到閾值,而後觸發GIL的釋放與再競爭(多個線程來回切換是須要消耗資源的),因此python下的多線程對 CPU密集型代碼並不友好,會觸發至關頻繁的線程切換。線程
IO密集型(文件處理、網絡爬蟲等),多線程可以有效提高效率(單線程下有IO操做會進行IO 等待,形成沒必要要的時間浪費,而開啓多線程能在線程A等待時,自動切換到線程B,能夠不浪費 CPU的資源,從而能提高程序執行效率,一個線程得到GIL發送消息,而後等待返回消息(阻塞),Python此時釋放GIL, 其餘線程獲得GIL發送消息,而後一樣等待返回消息(阻塞)......,這樣保證了IO傳輸過程時間的合理利用,減小了IO等待形成的資源浪費,提升IO傳輸效率)。因此python的多線程對IO密集型代碼比較友好。設計
I/O密集型使用多線程併發執行提升效率、計算密集型使用多進程(multiprocessing )並行執行提升效率。一般程序既包含IO操做又包含計算操做,那麼這種狀況下,在開始併發任務以前,能夠先進行測試,測試多線程、多進程哪一個效率高就是用哪一種方式。協程
請注意:多核多線程比單核多線程更差,多核多進程下,CPU1釋放GIL後,其餘CPU上的線程都會進行競爭,但GIL可能會立刻又被CPU1拿到,CPU2釋放GIL後……,致使其餘幾個CPU上被喚醒後的線程會醒着等待到切換時間後又進入待調度狀態,這樣會形成線程顛簸(thrashing),致使效率更低。
多線程下的CPU密集型計算也不是無藥可醫,能夠利用ctypes繞過GIL,ctypes可使py直接調用任意的C動態庫的導出函數。所要作的只是把關鍵部分用 C/C++ 寫成 Python 擴展。並且,ctypes會在調用C函數前釋放GIL。
同時,能夠了解下協程,又稱微線程。
協程最大的優點就是協程極高的執行效率。由於子程序切換不是線程切換,而是由程序自身控制,所以,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優點就越明顯。
第二大優點就是不須要多線程的鎖機制,由於只有一個線程,也不存在同時寫變量衝突,在協程中控制共享資源不加鎖,只須要判斷狀態就行了,因此執行效率比多線程高不少。
由於協程是一個線程執行,那怎麼利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可得到極高的性能。
轉自
Python多進程和多線程是雞肋嘛? https://www.toutiao.com/a6738231652184490247/