python3.x Day6 協程

協程:
#定義來自牛人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

  1. 必須在只有一個單線程裏實現併發
  2. 修改共享數據不需加鎖
  3. 用戶程序裏本身保存多個控制流的上下文棧
  4. 一個協程遇到IO操做自動切換到其它協程



源生的協程
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))
相關文章
相關標籤/搜索