與greenlet的比較
greenlet已經(jīng)實(shí)現(xiàn)了協(xié)程播揪,但是這個還的人工切換伶跷,是不是覺得太麻煩了辱揭,不要捉急扁凛,python還有一個比greenlet更強(qiáng)大的并且能夠自動切換任務(wù)的模塊gevent
其原理是當(dāng)一個greenlet遇到IO(指的是input output 輸入輸出盾沫,比如網(wǎng)絡(luò)裁赠、文件操作等)操作時,比如訪問網(wǎng)絡(luò)赴精,就自動切換到其他的greenlet佩捞,等到IO操作完成,再在適當(dāng)?shù)臅r候切換回來繼續(xù)執(zhí)行蕾哟。
由于IO操作非常耗時一忱,經(jīng)常使程序處于等待狀態(tài)莲蜘,有了gevent為我們自動切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行掀潮,而不是等待IO
1. gevent的使用
#coding=utf-8
#請使用python 2 來執(zhí)行此程序
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()
執(zhí)行結(jié)果:
<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是依次運(yùn)行而不是交替運(yùn)行
2.gevent切換執(zhí)行
import gevent
def f(n):
for i in range(n):
print gevent.getcurrent(), i
#用來模擬一個耗時操作,注意不是time模塊中的sleep
gevent.sleep(1)
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
執(zhí)行結(jié)果:
<Greenlet at 0x7fa70ffa1c30: f(5)> 0
<Greenlet at 0x7fa70ffa1870: f(5)> 0
<Greenlet at 0x7fa70ffa1eb0: f(5)> 0
<Greenlet at 0x7fa70ffa1c30: f(5)> 1
<Greenlet at 0x7fa70ffa1870: f(5)> 1
<Greenlet at 0x7fa70ffa1eb0: f(5)> 1
<Greenlet at 0x7fa70ffa1c30: f(5)> 2
<Greenlet at 0x7fa70ffa1870: f(5)> 2
<Greenlet at 0x7fa70ffa1eb0: f(5)> 2
<Greenlet at 0x7fa70ffa1c30: f(5)> 3
<Greenlet at 0x7fa70ffa1870: f(5)> 3
<Greenlet at 0x7fa70ffa1eb0: f(5)> 3
<Greenlet at 0x7fa70ffa1c30: f(5)> 4
<Greenlet at 0x7fa70ffa1870: f(5)> 4
<Greenlet at 0x7fa70ffa1eb0: f(5)> 4
3.gevent并發(fā)下載器
當(dāng)然仪吧,實(shí)際代碼里庄新,我們不會用gevent.sleep()去切換協(xié)程,而是在執(zhí)行到IO操作時薯鼠,gevent自動切換择诈,代碼如下
#coding=utf-8
from gevent import monkey;
import gevent
import urllib2
#有IO才做時需要這一句
monkey.patch_all()
def myDownLoad(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(myDownLoad, 'http://www.baidu.com/'),
gevent.spawn(myDownLoad, 'http://www.itcast.cn/'),
gevent.spawn(myDownLoad, 'http://www.itheima.com/'),
])
執(zhí)行結(jié)果:
GET: http://www.baidu.com/
GET: http://www.itcast.cn/
GET: http://www.itheima.com/
102247 bytes received from http://www.baidu.com/.
166903 bytes received from http://www.itheima.com/.
162294 bytes received from http://www.itcast.cn/.
從上能夠看到是先發(fā)送的獲取baidu的相關(guān)信息,然后依次是itcast出皇、itheima羞芍,但是收到數(shù)據(jù)的先后順序不一定與發(fā)送順序相同,這也就體現(xiàn)出了異步郊艘,即不確定什么時候會收到數(shù)據(jù)荷科,順序不一定
4.利用gevent實(shí)現(xiàn)TCP服務(wù)器
import sys
import time
import gevent
from gevent import socket,monkey
monkey.patch_all()
def handle_request(conn):
while True:
data = conn.recv(1024)
if not data:
conn.close()
break
print("recv: %s"%data)
conn.send(data)
def sever(port):
s = socket.socket()
s.bind(("", port))
s.listen(5)
while True:
cli, addr = s.accept()
gevent.spawn(handle_request, cli)
if __name__ == "__main__":
sever(7788)
執(zhí)行結(jié)果:
recv: b'http://www.cmsoft.cn QQ:10865600'
recv: b'http://www.cmsoft.cn QQ:10865600'
實(shí)現(xiàn)!!