Python的gevent协程及协程概念
https://www.cnblogs.com/tkqasn/p/5705338.html
何為協(xié)程
協(xié)程,又稱微線程。英文名Coroutine。
協(xié)程最大的優(yōu)勢就是協(xié)程極高的執(zhí)行效率。因為子程序切換不是線程切換,而是由程序自身控制,因此,沒有線程切換的開銷,和多線程比,線程數(shù)量越多,協(xié)程的性能優(yōu)勢就越明顯。
第二大優(yōu)勢就是不需要多線程的鎖機制,因為只有一個線程,也不存在同時寫變量沖突,在協(xié)程中控制共享資源不加鎖,只需要判斷狀態(tài)就好了,所以執(zhí)行效率比多線程高很多。
因為協(xié)程是一個線程執(zhí)行,那怎么利用多核CPU呢?最簡單的方法是多進程+協(xié)程,既充分利用多核,又充分發(fā)揮協(xié)程的高效率,可獲得極高的性能。后續(xù)會就這一塊單獨開寫一篇協(xié)程+多進程的測試文章。
Python對協(xié)程的支持還非常有限,用在generator中的yield可以一定程度上實現(xiàn)協(xié)程。雖然支持不完全,但已經(jīng)可以發(fā)揮相當(dāng)大的威力了。
使用生成器的例子
傳統(tǒng)的生產(chǎn)者-消費者模型是一個線程寫消息,一個線程取消息,通過鎖機制控制隊列和等待,但一不小心就可能死鎖。
如果改用協(xié)程,生產(chǎn)者生產(chǎn)消息后,直接通過yield跳轉(zhuǎn)到消費者開始執(zhí)行,待消費者執(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)運行結(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ù)是一個generator(生成器),把一個consumer傳入produce后:
首先調(diào)用c.next()啟動生成器;
然后,一旦生產(chǎn)了東西,通過c.send(n)切換到consumer執(zhí)行;
consumer通過yield拿到消息,處理,又通過yield把結(jié)果傳回;
produce拿到consumer處理的結(jié)果,繼續(xù)生產(chǎn)下一條消息;
produce決定不生產(chǎn)了,通過c.close()關(guān)閉consumer,整個過程結(jié)束。
整個流程無鎖,由一個線程執(zhí)行,produce和consumer協(xié)作完成任務(wù),所以稱為“協(xié)程”,而非線程的搶占式多任務(wù)。
使用gevent模塊
Python通過yield提供了對協(xié)程的基本支持,但是不完全。而第三方的gevent為Python提供了比較完善的協(xié)程支持。
gevent是第三方庫,通過greenlet實現(xiàn)協(xié)程,其基本思想是:
當(dāng)一個greenlet遇到IO操作時,比如訪問網(wǎng)絡(luò),就自動切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r候切換回來繼續(xù)執(zhí)行。由于IO操作非常耗時,經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動切換協(xié)程,就保證總有g(shù)reenlet在運行,而不是等待IO。
由于切換是在IO操作時自動完成,所以gevent需要修改Python自帶的一些標(biāo)準(zhǔn)庫,這一過程在啟動時通過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個greenlet是依次運行而不是交替運行。
要讓greenlet交替運行,可以通過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()運行結(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)> 23個greenlet交替運行,
把循環(huán)次數(shù)改為500000,讓它們的運行時間長一點,然后在操作系統(tǒng)的進程管理器中看,線程數(shù)只有1個。
當(dāng)然,實際代碼里,我們不會用gevent.sleep()去切換協(xié)程,而是在執(zhí)行到IO操作時,gevent自動切換,代碼如下:
from gevent import monkey; monkey.patch_all()#有IO才做時需要這一句 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個網(wǎng)絡(luò)操作是并發(fā)執(zhí)行的,而且結(jié)束順序不同,但只有一個線程。
小結(jié)
使用gevent,可以獲得極高的并發(fā)性能,但gevent只能在Unix/Linux下運行,在Windows下不保證正常安裝和運行,在windows下需要安裝第三方編譯好的包,或者自行編譯。
總結(jié)
以上是生活随笔為你收集整理的Python的gevent协程及协程概念的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 4、校验工具
- 下一篇: 200多个电脑修复工具问你要不要?