首先關於在python中單線程,多線程,多進程對cpu的利用率實測如下:
單線程,多線程,多進程測試代碼使用死循環。
1)單線程:


2)多線程:


3)多進程:


查看cpu使用效率:


開始觀察分別執行時候cpu的使用效率:
1)單線程執行的時候:

2)多線程執行的時候:

3)多進程執行的時候:

總結:
1)單進程單線程時,對於雙核CPU的利用率只能利用一個核,沒有充分利用兩個核。
2)單進程多線程時,對於雙核CPU的來說,雖然兩個核都用到的,不過很明顯沒有充分利用兩個核,這里要說一個GIL(全局解釋器鎖)的概念:
GIL不同於線程之間的互斥鎖,GIL並不是Python的特性,而是Cpython引入的一個概念。(Jpython,PYPY)
Python的代碼由Python的解釋器執行(CPython)。那么我們的代碼什么時候被python解釋器執行,由我們的GIL也就是全局解釋器鎖進行控制。
當我們有一個線程開始訪問解釋器的時候,GIL會將這把鎖上鎖,也就是說,其他線程無法再訪問解釋器,也就意味着,其他的線程無法再被執行。
GIL執行流程:
-
加鎖GIL。
-
切換到一個線程去執行。
-
運行。
-
解鎖GIL。
再次重復以上步驟。
對於下列代碼GIL的執行流程:
import threading
import time
# 寫兩個函數,分別讓兩個線程去執行
# 這個兩個函數,都要訪問我的全局變量
number = 0
def test1(count):
global number
for i in range(count):
number += 1
print(number)
def test2(count):
global number
for i in range(count):
number += 1
print(number)
def main():
th1 = threading.Thread(target=test1,args= (1000000,))
th2 = threading.Thread(target=test2, args=(1000000,))
th1.start()
th2.start()
time.sleep(5)
print(number)
if __name__ == '__main__':
main()
運行結果(這里充分的說明了多線程資源搶占問題):

流程圖如下:

線程1在執行到對全局變量加一操作的時候全局解釋器鎖被收回,線程2申請並得到了全局解釋器鎖開始運行,在線程2執行完加一操作以后對全局變量進行了修改並釋放了全局解釋器鎖。
這時線程1再次得到了全局解釋器鎖,從上次釋放全局解釋器鎖的地方開始繼續執行對全局變量加一的操作,記住,這里線程1中的全局變量還是開始的0,雖然線程2已經對其進行了加一的操作,但是線程1並不知道,線程1還是會接着上一次的位置開始執行,所以線程1在執行完加一操作的時候同樣把1再次賦值給了全局變量num,也就是說,線程2執行完加一操作之后賦值過去的1又被線程1賦值過去的1所覆蓋,加了兩次等於加了一次!類似於協程,只是做了一個執行代碼來回切換的操作!
所以在Python中,同一時刻,只能有一個線程被執行。所以Python中的多線程是假的。
既然這樣我們為什么還要用多線程呢?
其實多線程也有它的好處,例如我們在進行IO操作的時候,有效的組織了程序的阻塞,不至於一直無限的等待。
3)多進程時,對於雙核CPU來說,每個進程的優先級都是同等的,所分配的資源也是相等的,兩個進程的時候完全可以充分的利用雙核CPU,而且由於計算密集型的任務完全是依靠於cpu的核數,所以需要盡量的完全利用cpu,這時候多進程的好處就能夠完美的體現出來。
