日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Python的gevent协程及协程概念

發(fā)布時(shí)間:2023/12/9 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python的gevent协程及协程概念 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

https://www.cnblogs.com/tkqasn/p/5705338.html

何為協(xié)程

協(xié)程,又稱(chēng)微線程。英文名Coroutine。

協(xié)程最大的優(yōu)勢(shì)就是協(xié)程極高的執(zhí)行效率。因?yàn)樽映绦蚯袚Q不是線程切換,而是由程序自身控制,因此,沒(méi)有線程切換的開(kāi)銷(xiāo),和多線程比,線程數(shù)量越多,協(xié)程的性能優(yōu)勢(shì)就越明顯。

第二大優(yōu)勢(shì)就是不需要多線程的鎖機(jī)制,因?yàn)橹挥幸粋€(gè)線程,也不存在同時(shí)寫(xiě)變量沖突,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了,所以執(zhí)行效率比多線程高很多。

因?yàn)閰f(xié)程是一個(gè)線程執(zhí)行,那怎么利用多核CPU呢?最簡(jiǎn)單的方法是多進(jìn)程+協(xié)程,既充分利用多核,又充分發(fā)揮協(xié)程的高效率,可獲得極高的性能。后續(xù)會(huì)就這一塊單獨(dú)開(kāi)寫(xiě)一篇協(xié)程+多進(jìn)程的測(cè)試文章。

Python對(duì)協(xié)程的支持還非常有限,用在generator中的yield可以一定程度上實(shí)現(xiàn)協(xié)程。雖然支持不完全,但已經(jīng)可以發(fā)揮相當(dāng)大的威力了。

使用生成器的例子

傳統(tǒng)的生產(chǎn)者-消費(fèi)者模型是一個(gè)線程寫(xiě)消息,一個(gè)線程取消息,通過(guò)鎖機(jī)制控制隊(duì)列和等待,但一不小心就可能死鎖。

如果改用協(xié)程,生產(chǎn)者生產(chǎn)消息后,直接通過(guò)yield跳轉(zhuǎn)到消費(fèi)者開(kāi)始執(zhí)行,待消費(fèi)者執(zhí)行完畢后,切換回生產(chǎn)者繼續(xù)生產(chǎn),效率極高:

import timedef consumer():r = ''while True:n = yield rif not n:returnprint('[CONSUMER] Consuming %s...' % n)time.sleep(1)r = '200 OK'def produce(c):c.next()n = 0while n < 5:n = n + 1print('[PRODUCER] Producing %s...' % n)r = c.send(n)print('[PRODUCER] Consumer return: %s' % r)c.close()if __name__=='__main__':c = consumer()produce(c)

運(yùn)行結(jié)果

[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK

注意到consumer函數(shù)是一個(gè)generator(生成器),把一個(gè)consumer傳入produce后:

  • 首先調(diào)用c.next()啟動(dòng)生成器;

  • 然后,一旦生產(chǎn)了東西,通過(guò)c.send(n)切換到consumer執(zhí)行;

  • consumer通過(guò)yield拿到消息,處理,又通過(guò)yield把結(jié)果傳回;

  • produce拿到consumer處理的結(jié)果,繼續(xù)生產(chǎn)下一條消息;

  • produce決定不生產(chǎn)了,通過(guò)c.close()關(guān)閉consumer,整個(gè)過(guò)程結(jié)束。

  • 整個(gè)流程無(wú)鎖,由一個(gè)線程執(zhí)行,produce和consumer協(xié)作完成任務(wù),所以稱(chēng)為“協(xié)程”,而非線程的搶占式多任務(wù)。

    使用gevent模塊

    Python通過(guò)yield提供了對(duì)協(xié)程的基本支持,但是不完全。而第三方的gevent為Python提供了比較完善的協(xié)程支持。

    gevent是第三方庫(kù),通過(guò)greenlet實(shí)現(xiàn)協(xié)程,其基本思想是:

    當(dāng)一個(gè)greenlet遇到IO操作時(shí),比如訪問(wèn)網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來(lái)繼續(xù)執(zhí)行。由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO。

    由于切換是在IO操作時(shí)自動(dòng)完成,所以gevent需要修改Python自帶的一些標(biāo)準(zhǔn)庫(kù),這一過(guò)程在啟動(dòng)時(shí)通過(guò)monkey patch完成:

    import geventdef f(n):for i in range(n):print gevent.getcurrent(), ig1 = gevent.spawn(f, 5) g2 = gevent.spawn(f, 5) g3 = gevent.spawn(f, 5) g1.join() g2.join() g3.join() <Greenlet at 0x10e49f550: f(5)> 0 <Greenlet at 0x10e49f550: f(5)> 1 <Greenlet at 0x10e49f550: f(5)> 2 <Greenlet at 0x10e49f550: f(5)> 3 <Greenlet at 0x10e49f550: f(5)> 4 <Greenlet at 0x10e49f910: f(5)> 0 <Greenlet at 0x10e49f910: f(5)> 1 <Greenlet at 0x10e49f910: f(5)> 2 <Greenlet at 0x10e49f910: f(5)> 3 <Greenlet at 0x10e49f910: f(5)> 4 <Greenlet at 0x10e49f4b0: f(5)> 0 <Greenlet at 0x10e49f4b0: f(5)> 1 <Greenlet at 0x10e49f4b0: f(5)> 2 <Greenlet at 0x10e49f4b0: f(5)> 3 <Greenlet at 0x10e49f4b0: f(5)> 4

    可以看到,3個(gè)greenlet是依次運(yùn)行而不是交替運(yùn)行。

    要讓greenlet交替運(yùn)行,可以通過(guò)gevent.sleep()交出控制權(quán):

    import gevent import randomdef f(n):for i in range(n):print gevent.getcurrent(), igevent.sleep(random.randint(0,4))g1 = gevent.spawn(f, 3) g2 = gevent.spawn(f, 3) g3 = gevent.spawn(f, 3) g1.join() g2.join() g3.join() 

    運(yùn)行結(jié)果

    <Greenlet at 0x2682c48L: func(3)> 0 <Greenlet at 0x2682e48L: func(3)> 0 <Greenlet at 0x29d9548L: func(3)> 0 <Greenlet at 0x2682c48L: func(3)> 1 <Greenlet at 0x29d9548L: func(3)> 1 <Greenlet at 0x29d9548L: func(3)> 2 <Greenlet at 0x2682c48L: func(3)> 2 <Greenlet at 0x2682e48L: func(3)> 1 <Greenlet at 0x2682e48L: func(3)> 2

    3個(gè)greenlet交替運(yùn)行,

    把循環(huán)次數(shù)改為500000,讓它們的運(yùn)行時(shí)間長(zhǎng)一點(diǎn),然后在操作系統(tǒng)的進(jìn)程管理器中看,線程數(shù)只有1個(gè)。

    當(dāng)然,實(shí)際代碼里,我們不會(huì)用gevent.sleep()去切換協(xié)程,而是在執(zhí)行到IO操作時(shí),gevent自動(dòng)切換,代碼如下:

    from gevent import monkey; monkey.patch_all()#有IO才做時(shí)需要這一句 import gevent import urllib2def f(url):print('GET: %s' % url)resp = urllib2.urlopen(url)data = resp.read()print('%d bytes received from %s.' % (len(data), url))gevent.joinall([gevent.spawn(f, 'https://www.python.org/'),gevent.spawn(f, 'https://www.yahoo.com/'),gevent.spawn(f, 'https://github.com/'), ])  GET: https://www.python.org/ GET: https://www.yahoo.com/ GET: https://github.com/ 45661 bytes received from https://www.python.org/. 14823 bytes received from https://github.com/. 304034 bytes received from https://www.yahoo.com/.

    從結(jié)果看,3個(gè)網(wǎng)絡(luò)操作是并發(fā)執(zhí)行的,而且結(jié)束順序不同,但只有一個(gè)線程。

    小結(jié)

    使用gevent,可以獲得極高的并發(fā)性能,但gevent只能在Unix/Linux下運(yùn)行,在Windows下不保證正常安裝和運(yùn)行,在windows下需要安裝第三方編譯好的包,或者自行編譯。

    總結(jié)

    以上是生活随笔為你收集整理的Python的gevent协程及协程概念的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。