協(xié)程

生成器語法

yield ??? 一個對象
??????????????????????返回這個對象
??????????????????????暫停這個函數(shù)
??????????????????????等待下次next重新激活

def my_gen():
    print('第一次執(zhí)行')
    a = yield 1
    print('a is:', a)
    print('第二次執(zhí)行')
    b = yield 2
    print('b is:', b)
    print('第三次執(zhí)行')
    c = yield 3
    print('c is:', c)

g = my_gen()
v1 = next(g)
print(v1)
v2 = next(g)
print(v2)
v3 = next(g)
print(v3)

運行結(jié)果:
image.png

send與yield的切換

send 一個對象
??????????????????????激活生成器
??????????????????????執(zhí)行生成器里面的代碼
??????????????????????遇到y(tǒng)ield回到調(diào)用位置

def my_gen():
    print('第一次執(zhí)行')
    a = yield 1
    print('a is:', a)
    print('第二次執(zhí)行')
    b = yield 2
    print('b is:', b)
    print('第三次執(zhí)行')
    c = yield 3
    print('c is:', c)


g = my_gen()
v1 = next(g)   #相當(dāng)于g.send(None) 娩贷,生成器的啟動需要next或者send(None)嗜浮,否則報錯

print(v1)
v2 = g.send('cainiao')
print(v2)
v3 = g.send('xiaobai')
print(v3)

運行結(jié)果:
image.png

生產(chǎn)者/消費者模型

import random
import time

#消費者
def consumer():
    while True:
        data = yield
        print('消費者拿到:', data)

#生產(chǎn)者
def producer(cos):
    next(cos)       #激活生成器對象乳愉,讓他執(zhí)行到y(tǒng)ield
    while True:
        item = random.randint(0, 99)     #隨機(jī)生產(chǎn)一個數(shù)
        print('生產(chǎn)者生產(chǎn)了:', item)
        cos.send(item)     #把item給消費者撰糠,切換到消費者函數(shù)
        time.sleep(2)

c = consumer()      #返回一個生成器對象
producer(c)

運行結(jié)果:
image.png

注意事項:

  • 對一個生成器必須要先next()讓他執(zhí)行到y(tǒng)ield才能在send數(shù)據(jù)進(jìn)去囱淋。
  • 攜程是在一個線程內(nèi)的執(zhí)行的竿秆,本質(zhì)來說就是不同函數(shù)之間的切換調(diào)用成榜。
  • 如果某一個協(xié)程被阻塞了,整個線程(進(jìn)程)都被阻塞末早。任意時刻,只有一個協(xié)程在執(zhí)行说庭。

greenlet

  • 雖然CPython(標(biāo)準(zhǔn)Python)能夠通過生成器來實現(xiàn)協(xié)程然磷,但使用起來還并不是很方便。
  • Python的一個衍生版 Stackless Python刊驴,實現(xiàn)了原生的協(xié)程姿搜,它更利于使用。于是捆憎,大家開始將 Stackless 中關(guān)于協(xié)程的代碼舅柜,單獨拿出來做成了CPython的擴(kuò)展包。
    這就是 greenlet 的由來躲惰,greenlet 是底層實現(xiàn)了原生協(xié)程的 C擴(kuò)展庫致份。
    需要下載安裝此模塊
    pip install greenlet
    用greenlet重寫上述代碼:
from greenlet import greenlet
import random
import time

#消費者
def consumer():
    while True:
        item = p.switch()      #切換到生產(chǎn)者,并等待生產(chǎn)者傳入item
        print('消費者拿到:', item)

#生產(chǎn)者
def producer():
    while True:
        item = random.randint(0, 99)     #隨機(jī)生產(chǎn)一個數(shù)
        print('生產(chǎn)者生產(chǎn)了:', item)
        c.switch(item)     #將item傳給消費者础拨,切換到消費者函數(shù)
        time.sleep(2)      #每生產(chǎn)一個就休息一些氮块,方便我們看效果

c = greenlet(consumer)  #將一個普通的函數(shù)變成協(xié)程
p = greenlet(producer)
c.switch()   #運行消費者

運行結(jié)果:
image.png

greenlet作用

  1. 高性能的原生協(xié)程;
  2. 語義更加明確的顯式切換诡宗;
  3. 直接將函數(shù)包裝成協(xié)程滔蝉,保持原有代碼風(fēng)格。
gevent協(xié)程

雖然塔沃,我們有了 基于 epoll 的回調(diào)式編程模式蝠引,但是卻難以使用。即使我們可以通過配合 生成器協(xié)程 進(jìn)行復(fù)雜的封裝蛀柴,以簡化編程難度螃概。但是仍然有一個大的問題: 封裝難度大,現(xiàn)有代碼幾乎完全要重寫gevent名扛,通過封裝了 libev(基于epoll) 和 greenlet 兩個庫谅年。幫我們做好封裝,允許我們以類似于線程的方式使用協(xié)程肮韧。以至于我們幾乎不用重寫原來的代碼就能充分利用 epoll 和 協(xié)程 威力融蹂。
安裝:pip install gevent

import gevent

from gevent import monkey
monkey.patch_socket()       #打了這個補(bǔ)丁,下面導(dǎo)入的socket都會被epoll監(jiān)控
import socket

def func(conn):
    while True:
        data = conn.recv(1024)
        if data:
            print('接受的數(shù)據(jù)%s'%data.decode())
            conn.send(data)
        else:
            conn.close()
            break

server = socket.socket()
server.bind(('0.0.0.0', 7001))
server.listen()
print('等待連接......')

while True:
    conn, addr = server.accept()
    print('{}上線'.format(addr))
    gevent.spawn(func, conn)     #創(chuàng)建一個協(xié)程

運行結(jié)果:


image.png

image.png

image.png

gevent的作用

遇到阻塞就切換到另一個協(xié)程繼續(xù)執(zhí)行 弄企!

  1. 使用基于 epoll 的 libev 來避開阻塞超燃;
  2. 使用基于 gevent 的 高效協(xié)程 來切換執(zhí)行;
  3. 只在遇到阻塞的時候切換拘领,沒有輪需的開銷意乓,也沒有線程的開銷;

gevent協(xié)程通信

  • 問題一:協(xié)程之間不是能通過switch通信嘛约素?
    是的届良,由于 gevent 基于 greenlet笆凌,所以可以。
  • 問題二:那為什么還要考慮通信問題士葫?
    因為 gevent 不需要我們使用手動切換乞而,而是遇到阻塞就切換,因此我們不會去使用switch 慢显!

gevent.queue.Queue
image.png

總結(jié):
充分利用cpu多進(jìn)程爪模,用線程來做計算密集型任務(wù),gevent來做IO密集型任務(wù)荚藻。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屋灌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子应狱,更是在濱河造成了極大的恐慌共郭,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疾呻,死亡現(xiàn)場離奇詭異落塑,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)罐韩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來污朽,“玉大人散吵,你說我怎么就攤上這事◇∷粒” “怎么了矾睦?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長炎功。 經(jīng)常有香客問我枚冗,道長,這世上最難降的妖魔是什么蛇损? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任赁温,我火速辦了婚禮,結(jié)果婚禮上淤齐,老公的妹妹穿的比我還像新娘股囊。我一直安慰自己,他們只是感情好更啄,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布稚疹。 她就那樣靜靜地躺著,像睡著了一般祭务。 火紅的嫁衣襯著肌膚如雪内狗。 梳的紋絲不亂的頭發(fā)上怪嫌,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音柳沙,去河邊找鬼岩灭。 笑死,一個胖子當(dāng)著我的面吹牛偎行,可吹牛的內(nèi)容都是我干的川背。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼蛤袒,長吁一口氣:“原來是場噩夢啊……” “哼熄云!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起妙真,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤缴允,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后珍德,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體练般,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年锈候,在試婚紗的時候發(fā)現(xiàn)自己被綠了薄料。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡泵琳,死狀恐怖摄职,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情获列,我是刑警寧澤谷市,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站击孩,受9級特大地震影響迫悠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巩梢,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一创泄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧括蝠,春花似錦验烧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春感混,著一層夾襖步出監(jiān)牢的瞬間端幼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工弧满, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留婆跑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓庭呜,卻偏偏與公主長得像滑进,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子募谎,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容