簡介
沒有切換開銷。因為子程序切換不是線程切換,而是由程序自身控制,沒有線程切換的開銷,因此執行效率高,
不需要鎖機制。因為只有一個線程,也不存在同時寫變量沖突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多線程高很多
Python對協程的支持還非常有限,用在generator中的yield可以一定程度上實現協程。
yield
傳統的生產者-消費者模型是一個線程寫消息,一個線程取消息,通過鎖機制控制隊列和等待,但一不小心就可能死鎖。
如果改用協程,生產者生產消息后,直接通過yield跳轉到消費者開始執行,待消費者執行完畢后,切換回生產者繼續生產,效率極高
代碼
import time
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s....' % n)
r = '200 OK'
def produce(c):
c.next()
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s\n' % r)
c.close()
if __name__=='__main__':
c = consumer()
produce(c)
結果
[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
分析
- 首先調用c.next()啟動生成器
- 然后,一旦生產了東西,通過c.send(n)切換到consumer執行
- consumer通過yield拿到消息,處理,又通過yield把結果傳回
- produce拿到consumer處理的結果,繼續生產下一條消息
整個過程無鎖,由一個線程執行,producer和consumer寫作完成任務,所以叫做協程
gevent
Python通過yield提供了對協程的基本支持,但是不完全。而第三方的gevent為Python提供了比較完善的協程支持
gevent是第三方庫,通過greenlet實現協程,其基本思想是:
當一個greenlet遇到IO操作時(比如訪問網絡),就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續執行。由于IO操作非常耗時,經常使程序處于等待狀態,有了gevent為我們自動切換協程,就保證總有greenlet在運行,而不是等待IO。
import gevent
def f(n):
for i in range(n):
print gevent.getcurrent(), i
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
結果
0
1
2
3
4
0
1
2
3
4
0
1
2
3
4
可以看出3個greenlet依次運行,而不是交替運行
要讓greenlet交替運行,可以通過gevent.sleep()交出控制權
import gevent
def f(n):
for i in range(n):
print gevent.getcurrent(), i
gevent.sleep(1)
g1 = gevent.spawn(f, 3)
g2 = gevent.spawn(f, 3)
g3 = gevent.spawn(f, 3)
g1.join()
g2.join()
g3.join()
結果
0
0
0
1
1
1
2
2
2
可以看出3個greenlet是交替執行
如果把循環改為1000,讓執行次數執行時間長些,查看進程,可以看到線程只有一個。
當然,實際代碼中,不可能用gevent.sleep()去切換協程,而是在執行IO操作是,gevent自動切換,參考代碼如下
import gevent
from gevent import monkey; monkey.patch_all()
import urllib2
def f(url):
print 'GET: %s' % url
resp = urllib2.urlopen(url)
data = resp.read()
print '[%d] bytes received from %s\n' %(len(data), url)
gevent.joinall([
gevent.spawn(f, 'http://www.cnblogs.com/kaituorensheng/'),
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.baidu.com'),
])
執行結果
GET: http://www.cnblogs.com/kaituorensheng/
GET: https://www.python.org/
GET: https://www.baidu.com
[227] bytes received from https://www.baidu.com
[14667] bytes received from http://www.cnblogs.com/kaituorensheng/
[47348] bytes received from https://www.python.org/
可以看到3個url結束順序并不是依次執行完的。
注
使用gevent,可以獲得極高的并發性能,但gevent只能在Unix/Linux下運行,在Windows下不保證正常安裝和運行。
由于gevent是基于IO切換的協程,所以最神奇的是,我們編寫的Web App代碼,不需要引入gevent的包,也不需要改任何代碼,僅僅在部署的時候,用一個支持gevent的WSGI服務器,立刻就獲得了數倍的性能提升。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

