python的執行緒雖然是真正的執行緒,但直譯器執行**時,有乙個gil鎖:global interpreter lock,任何python執行緒執行前,必須先獲得gil鎖,然後,每執行100條位元組碼,直譯器就自動釋放gil鎖,讓別的執行緒有機會執行。這個gil全域性鎖實際上把所有執行緒的執行**都給上了鎖,所以,多執行緒在python中只能交替執行,即使100個執行緒跑在100核cpu上,也只能用到1個核。
但是對於io密集型的任務,多執行緒還是起到很大效率提公升,這是協同式多工
當一項任務比如網路 i/o啟動,而在長的或不確定的時間,沒有執行任何 python **的需要,乙個執行緒便會讓出gil,從而其他執行緒可以獲取 gil 而執行 python。這種禮貌行為稱為協同式多工處理,它允許併發;多個執行緒同時等待不同事件。
乙個執行緒的執行時間可以分為3部分:執行緒的啟動時間、執行緒體的執行時間和執行緒的銷毀時間。在多執行緒處理的情景中,如果執行緒不能被重用,就意味著每次建立都需要經過啟動、銷毀和執行3個過程。這必然會增加系統相應的時間,降低了效率。
對於任務數量不斷增加的程式,每有乙個任務就生成乙個執行緒,最終會導致執行緒數量的失控,例如,整站爬蟲,假設初始只有乙個鏈結a,那麼,這個時候只啟動乙個執行緒,執行之後,得到這個鏈結對應頁面上的b,c,d,,,等等新的鏈結,作為新任務,這個時候,就要為這些新的鏈結生成新的執行緒,執行緒數量暴漲。在之後的執行中,執行緒數量還會不停的增加,完全無法控制。所以,對於任務數量不端增加的程式,固定執行緒數量的執行緒池是必要的。
python3中自帶了concurrent.futures模組,可以實現執行緒池
from concurrent.futures import threadpoolexecutor
import time
import random
from concurrent.futures import threadpoolexecutor
import time
def hello(num):
time.sleep(random.randint(1, 3))
print("{}只小鴨子,咿呀咿呀呦".format(num))
if __name__ == "__main__":
excutor1 = threadpoolexecutor(max_workers=3)
for i in range(1,8):
future = excutor1.submit(hello,i)
future.done()
with threadpoolexecutor(3) as executor1:
executor1.map(hello, [1,2,3])
在提交任務的時候,有兩種方式,一種是submit()函式,另一種是map()函式,兩者的主要區別在於:
協程:又稱微執行緒,纖程,是一種使用者態的輕量級執行緒
執行緒是系統級別的,他們是由作業系統排程。協程是程式級別的,由程式設計師根據需求自己排程。我們把乙個執行緒中的乙個個函式稱為子程式,那麼子程式在執行的過程中可以中斷去執行別的子程式。別的子程式也可以中斷回來繼續執行之前的子程式,這就是協程。也就是說同一執行緒下的一段**1執行執行著就可以中斷,然後去執行另一段**2,當再次回來執行**塊1的時候,接著從之前中斷的地方開始執行
子程式:在所有的語言中都是層級呼叫,比如a中呼叫b,b在執行過程中呼叫c,c執行完返回,b執行完返回,最後是a執行完畢。是通過棧實現的,乙個執行緒就是執行乙個子程式,子程式的呼叫總是有乙個入口,一次返回,呼叫的順序是明確的
缺點:python對協程的支援是通過generator實現的
def demo():
print('-> 開始')
x = yield #yield將接收send過來的值2賦值給x;並返回none
print('-> 接收', x) #列印 -> 接收 2
c = yield (1 + x) #接收send過來的3,並把3返回
print("x,c:",x,c) #列印 x,c: 2 3
sc = demo(1) #生成協程物件
#next將啟用協程執行到第乙個yield
print(next(sc)) #獲取到的返回值none,列印none
print(sc.send(2)) #列印3
sc.send(3) #下面沒有yield,丟擲異常stopiteration
next作用啟用協程執行到下乙個yield,等價於sc.send(none)
協程在執行過程中有四個狀態:
from inspect import getgeneratorstate
print(getgeneratorstate(sc))
sc.send(none)
print(getgeneratorstate(sc))
sc.send(2)
print(getgeneratorstate(sc))
sc.send(3)
print(getgeneratorstate(sc))
傳統的生產者-消費者模型是乙個執行緒寫訊息,乙個執行緒取訊息,通過鎖機制控制佇列和等待,但一不小心就可能死鎖。
如果改用協程,生產者生產訊息後,直接通過yield
跳轉到消費者開始執行,待消費者執行完畢後,切換回生產者繼續生產,效率極高
import threading
import time
def product(c):
print(threading.currentthread().name)
c.send(none)
for i in range(1,4):
print("蒸包子:{}".format(i))
res = c.send(i)
print("客戶反饋:{}".format(res))
time.sleep(1)
c.close()
def eat():
print(threading.currentthread().name)
res = ''
while true:
bz = yield res
if not bz:
return
print("吃包子:{}".format(bz))
res = "真好吃"
cs = eat()
product(cs)
執行緒佇列,執行緒池,協程
執行緒的queue,類似於程序 作用也是類似,queue n 規範放入值的數量 這個和之前一樣是為了實現先進先出 import queue q queue.queue 2 括號內可加入數字規範放入值的數量,不加則不會規範 q.put 123 q.put qweqwe q.put 111 print ...
執行緒佇列 執行緒池 協程
1 執行緒佇列 from multiprocessing queue joinablequeue 程序ipc佇列 from queue import queue 執行緒佇列 先進先出 from queue import lifoqueue 後進先出的 方法都是一樣的 put get put nowa...
python 執行緒,協程
本文 threading用於提供執行緒相關的操作。執行緒是應用程式中工作的最小單元,它被包含在程序之中,是程序中的實際運作單位。一條執行緒指的是程序中乙個單一順序的控制流,乙個程序中可以併發多個執行緒,每條執行緒並行執行不同的任務。threading 模組建立在 thread 模組之上。thread...