協程:
#定義來自牛人alex博客
協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種用戶態的輕量級線程。
協程擁有本身的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:
協程能保留上一次調用時的狀態(即全部局部狀態的一個特定組合),每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
使用協程的目的:就是爲了最大限度使用CPU,把IO操做解耦,提升程序運行速度
協程的好處:
無需線程上下文切換的開銷
無需原子操做鎖定及同步的開銷
"原子操做(atomic operation)是不須要synchronized",所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心。
方便切換控制流,簡化編程模型
高併發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。因此很適合用於高併發處理。
缺點:
沒法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程須要和進程配合才能運行在多CPU上.固然咱們平常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
進行阻塞(Blocking)操做(如IO時)會阻塞掉整個程序
greenlet是封裝好了的協程
gevent 是進一步封裝了greenlet
greenlet還須要經過greenlet.greenlet(func)生成greenlet類實例,只能經過gr1.swich()方法手動切換方法執行。
gevent經過gevent.spawn(func,args)生成greenlet類實例,經過gevent.joinall([])裝載Greenlet實例,便可啓動各個方法,並實現自動的遇到IO就切換
通常在整個當前程序前須要from gevent import monkey引入monkey包,這是個補丁,裏邊有monkey.patch_all()方法,是明確標記全部IO操做,遇到就切換,
主要是像socket、urllib中的IO操做不會被gevent直接發現時使用這個補丁就能發現了。
gevent須要自行安裝pip install gevent
源生的協程
import time import queue def consumer(name): print("--->starting eating baozi...") while True: new_baozi = yield print("[%s] is eating baozi %s" % (name,new_baozi)) #time.sleep(1) def producer(): r = con.__next__() r = con2.__next__() n = 0 while n < 5: n +=1 con.send(n) con2.send(n) print("\033[32;1m[producer]\033[0m is making baozi %s" %n ) if __name__ == '__main__': con = consumer("c1") con2 = consumer("c2") p = producer()
利用模塊greenlet寫協程,簡單一些了git
#用greenlet來寫個簡單的協程,你們猜一下運行結果 import greenlet #導入greenlet協程包 def green_func1(): #定義第一個協程用的方法 print(12) gr2.switch() #切換到協程gr2,保存現場,再度切換回來時,從這裏開始 print(34) gr2.switch() #切換到協程gr2,保存現場,再度切換回來時,從這裏開始 def green_func2(): #定義第二個協程用的方法 print(56) gr1.switch() #切換回協程gr1,保存現場,再度切換回來時,從這裏開始 print(78) gr1=greenlet.greenlet(green_func1) #定義第一個協程,裝入方法green_func1方法,並啓動協程gr1 gr2=greenlet.greenlet(green_func2) #定義第二個協程,裝入方法green_func2方法,並啓動協程gr2 gr1.switch() #手動切換到協程gr1開始執行。由於沒有執行過,因此切換時,從函數開頭執行
來個自動的切換吧,這纔是真正的寫法:github
#寫個協程抓個網頁看看 import gevent,time #引入協程模塊 from urllib import request from gevent import monkey #引入協程模塊下的monkey補丁模塊 monkey.patch_all() #使用monkey模塊下的patch_all()方法,做用是:在每一個urllib模塊的IO操做前增長標記,來明確協程切換時機 def f_get(url): #定義使用協程的方法, print("GET %s"%url) resp=request.urlopen(url) data=resp.read() print("%d bytes recevied from %s"%(len(data),url)) urls=[ "https://www.baidu.com/", "https://github.com/", "https://hub.docker.com/", "https://www.yahoo.com/" ] print("同步獲取:") ss_time=time.time() for u in urls: f_get(u) print("同步耗時:%s"%(time.time()-ss_time)) print("協程異步獲取:") async_time=time.time() gevent.joinall([ #定義協程的啓動,須要傳入一個gevent.spawn(其實就是Greenlet類的實例)列表 gevent.spawn(f_get,"https://www.baidu.com/"), gevent.spawn(f_get,"https://github.com/"), gevent.spawn(f_get,"https://hub.docker.com/"), gevent.spawn(f_get,"https://www.yahoo.com/") ]) print("協程異步耗時:%s"%(time.time()-async_time)) print("循環生成Greenlet實例變成列表協程異步獲取:") gl=[] for uu in urls: gl.append(gevent.spawn(f_get,uu)) async_time=time.time() gevent.joinall(gl)#定義協程的啓動,須要傳入一個gevent.spawn(其實就是Greenlet類的實例)列表 print("協程異步耗時:%s"%(time.time()-async_time))