協程的概念:
協程: 協助程序,線程和進程都是搶佔式特點,線程和進程的切換我們是不能參與的。
而協程是非搶佔式特點,協程也存在着切換,這種切換是由我們用戶來控制的。
協程主解決的是IO的操作。
協程,又稱微線程,纖程。英文名Coroutine。
協程的優點:
優點1: 協程極高的執行效率。因爲子程序切換不是線程切換,而是由程序自身控制,
因此,沒有線程切換的開銷,和多線程比,線程數量越多,協程的性能優勢就越明顯。
優點2: 不需要多線程的鎖機制,因爲只有一個線程,也不存在同時寫變量衝突,
在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多線程高很多。
因爲協程是一個線程執行,那怎麼利用多核CPU呢?簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可獲得極高的性能。
yield的簡單實現
yield的功能類似於return,但是不同之處在於它返回的是生成器。
生成器是通過一個或多個yield表達式構成的函數,每一個生成器都是一個迭代器(但是迭代器不一定是生成器)。
如果一個函數包含yield關鍵字,這個函數就會變成一個生成器。
生成器並不會一次返回所有的結果,而是每次遇到yield關鍵字後返回相應的結果,並保留函數當前的運行狀態,等待下一次的調用。
由於生成器也是一個迭代器,那麼他就應該支持next方法來獲取下一個值。
import time
def consumer(name):
print("消費者準備喫包子-----------")
while True:
new_baozi = yield #接受調用生成器的值
print("%s喫第%d個包子"%(name,new_baozi))
time.sleep(1)
def producer(name):
r=con.__next__()
r=con2.__next__()
count=1
while True:
print("%s正在生產f第%s個包子和第%d個包子"%(name,count,count+1))
#調用生成器並且發送數據
con.send(count)
con2.send(count+1)
count+=2
if __name__ == '__main__':
con=consumer("翠花")
con2=consumer("王大錘")
greenlet 模塊
greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可以使你在任意函數之間隨意切換,而不需把這個函數先聲明爲generator
安裝 :pip3 install greenlet。如果引入的時候還是報錯,使用pycharm進行下載
from greenlet import greenlet
def work1():
print(12)
gr2.switch()
print(45)
gr2.switch()
def work2():
print(89)
gr1.switch()
print(43)
# 1.將要執行的函數封裝到greenlet對象中
gr1 = greenlet(work1)
gr2 = greenlet(work2)
# 2.想先執行那個函數就可以使用 對象,swith()方法進行執行
gr1.switch()
結果:
12
89
45
43
gevent模塊
Gevent 是一個第三方庫,可以輕鬆通過gevent實現併發同步或異步編程,
在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。
Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
import time
import requests
import gevent
def f(url):
print("get",url)
resp=requests.get(url)
data = resp.text
print("%d bytes received from %s"%(len(data),url))
# 1.普通模式
s=time.time()
f("http://www.langlang2017.com/img/banner1.png")
f("http://www.langlang2017.com/img/banner2.png")
f("http://www.langlang2017.com/img/banner3.png")
f("http://www.langlang2017.com/img/banner4.png")
e=time.time()
print("普通模式時間",e-s)
# 2.使用gevent模塊
start = time.time()
gevent.joinall([gevent.spawn(f,"http://www.langlang2017.com/img/banner1.png"),
gevent.spawn(f, "http://www.langlang2017.com/img/banner2.png"),
gevent.spawn(f, "http://www.langlang2017.com/img/banner3.png"),
gevent.spawn(f, "http://www.langlang2017.com/img/banner4.png")])#創建一個普通的greenlet對象並切換
print("gevent時間",time.time()-start)
"""
如果量小的話串行比gevent
"""
結果:
get http://www.langlang2017.com/img/banner1.png
396098 bytes received from http://www.langlang2017.com/img/banner1.png
get http://www.langlang2017.com/img/banner2.png
666248 bytes received from http://www.langlang2017.com/img/banner2.png
get http://www.langlang2017.com/img/banner3.png
665348 bytes received from http://www.langlang2017.com/img/banner3.png
get http://www.langlang2017.com/img/banner4.png
755635 bytes received from http://www.langlang2017.com/img/banner4.png
普通模式時間 34.99790930747986
get http://www.langlang2017.com/img/banner1.png
396098 bytes received from http://www.langlang2017.com/img/banner1.png
get http://www.langlang2017.com/img/banner2.png
666248 bytes received from http://www.langlang2017.com/img/banner2.png
get http://www.langlang2017.com/img/banner3.png
665348 bytes received from http://www.langlang2017.com/img/banner3.png
get http://www.langlang2017.com/img/banner4.png
755635 bytes received from http://www.langlang2017.com/img/banner4.png
gevent時間 32.77941083908081
如果量小的話串行比gevent模塊花費時間可能少
如果大量數據就能顯示出他的性能了