python之協程的那些事

jerrysun發表於2021-09-09

python如何設定多程式()

協程

基本概念

協程,又稱微執行緒,纖程。英文名Coroutine。協程是一種使用者態的輕量級執行緒。

協程原理

協程擁有自己的暫存器上下文和棧。協程排程切換時,將暫存器上下文和棧儲存到其他地方,在切回來的時候,恢復先前儲存的暫存器上下文和棧。因此:協程能保留上一次呼叫時的狀態(即所有區域性狀態的一個特定組合),每次過程重入時,就相當於進入上一次呼叫的狀態,換種說法:進入上一次離開時所處邏輯流的位置。執行緒的切換,會儲存到CPU的暫存器裡。 CPU感覺不到協程的存在,協程是使用者自己控制的。之前透過yield做的生產者消費者模型,就是協程,在單執行緒下實現併發效果。

原理解讀

協程原理:利用一個執行緒,分解一個執行緒成為多個“微執行緒”==>程式級別 
如果寫爬蟲,就訪問別的網站,拿別人原始碼。http請求叫IO請求,用多執行緒。 
假設要訪問3個url,建立3個執行緒,都在等待著,第一個有資料返回就繼續執行,以此類推。 
在等待過程中,就什麼事也沒幹。

協程的方式。

計算機幫你建立程式、執行緒。執行緒是人為建立出來的。用一個執行緒,一會兒執行這個操作,一會兒執行那個操作。 
協程是隻用一個執行緒。程式設計師利用io多路複用的方式,讓協程: 
先訪問一個url,不等待返回,就再訪問第二個url,訪問第三個url,然後也在等待。 
greenlet本質是實現協程的。 
注意:協程本身不高效,協程的本質只是程式設計師呼叫的,那為啥gevent這麼高效率呢,是因為用了協程(greenlet)+IO多路複用的方式。 
是IO多路複用的用法才能高效。所以用的時候就用gevent就好了。 
#####協程的好處:

無需執行緒上下文切換的開銷 
無需資料操作鎖定及同步的開銷 
方便切換控制流,簡化程式設計模型 
高併發+高擴充套件性+低成本:一個CPU支援上萬的協程都不是問題。所以很適合用於高併發處理。

缺點:

無法利用多核資源:協程的本質是個單執行緒,它不能同時將 單個CPU 的多個核用上,協程需要和程式配合才能執行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。 
進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程式

應用場景:

IO密集型:用多執行緒+gevent(更好),多執行緒 
計算密集型:用多程式

案例解讀:

用多執行緒:假設每爬一個網址需要2秒,3個url,就是3個請求,等待2秒,就可以繼續往下走。 
如果用gevent,用單執行緒,單執行緒應該從上到下執行,用for迴圈讀取3個url,往地址傳送url請求,就是IO請求,執行緒是不等待的。 
for迴圈再拿第二個url,再發第三個url。在這過程中,誰先回來,就處理誰。 
資源佔用上,多執行緒佔用了3個執行緒,2秒鐘,多執行緒啥也沒幹,在等待。gevent在2秒鐘,只要傳送請求了,接著就想幹什麼幹什麼。 
案例:

from urllib import requestimport gevent, time# 注意!:Gevent檢測不到urllib的io操作,還是序列的,讓它知道就需要打補丁from gevent import monkey
monkey.patch_all()  # 把當前程式的所有IO操作單獨的做上標記def f(url):
    print("Get %s" %url)
    resp = request.urlopen(url)
    data = resp.read()    # with open("url.html", 'wb') as f:
    #     f.write(data)
    print("%d bytes received from %s" %(len(data), url))

print("非同步時間統計中……")  # 協程實現async_start_time = time.time()
gevent.joinall([
    gevent.spawn(f, ""),
    gevent.spawn(f, ""),
    gevent.spawn(f, ""),
])
print("

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/151/viewspace-2803290/,如需轉載,請註明出處,否則將追究法律責任。

相關文章