Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程。
用法:
g=gevent.spawn(func,1,……,x=3,……)
#创建一个协程对象g ,spawn括号内第一个参数是函数名,后面可以有多个参数(位置参数,关键字参数)。
g.join()#等待结束
g.value#拿到func的返回值。
遇到IO阻塞时会自动切换任务:
import gevent
def f1():
print('from f1 1')
gevent.sleep(3)
print('from f1 2')
def f2():
print('from f2 1')
gevent.sleep(2)
print('from f2 2')
def f3():
print('from f3 1')
gevent.sleep(2)
print('from f3 2')
g1=gevent.spawn(f1)
g2=gevent.spawn(f2)
g3=gevent.spawn(f3)
# g1.join()
# g2.join()
# g3.join()
gevent.joinall([g1,g2,g3])
上面的例子中gevent.sleep(2)模拟的是gevent可以识别的IO阻塞。
而time.sleep()或其它的阻塞,gevent是不能直接识别的,需要下面一行代码,打补丁识别。
from gevent import monkey;mokey.patch_all()
这一行代码必需放在打补丁者的前面。为了方便,不如直接方在文件的开头位置。
from gevent import monkey,spawn
monkey.patch_all()
import time
def f1():
print('from f1 1')
time.sleep(3)
print('from f1 2')
def f2():
print('from f2 1')
time.sleep(2)
print('from f2 2')
def f3():
print('from f3 1')
time.sleep(2)
print('from f3 2')
g1=spawn(f1)
g2=spawn(f2)
g3=spawn(f3)
g1.join()
g2.join()
g3.join()
接下来我们来通过gevent来实现一个单线程下的socket并发。
服务端:
from gevent import monkey ;monkey.patch_all()
from socket import *
import gevent
def server(server_ip,port):
s=socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind((server_ip,port))
s.listen(5)
while True:
conn,addr=s.accept()
gevent.spawn(talk,conn,addr)
def talk(conn,addr):
try:
while True:
res=conn.recv(1024)
print('client %s:%s msg:%s ' %(addr[0],addr[1],res))
conn.send(res.upper())
except Exception as e :
print(e)
finally:
conn.close()
if __name__ == '__main__':
server('127.0.0.1',8080)
客户端:
from threading import Thread
from socket import *
import threading
def client(server_ip,port):
c=socket(AF_INET,SOCK_STREAM)#套接字对象一定要加在函数内,不然其它线程共享,不然其它线程就共用一个对象,那么客户端端口就一样了。
c.connect((server_ip,port))
count=0
while True:
c.send(('%s say hello %s'%(threading.current_thread().getName(),count)).encode('utf-8'))
msg=c.recv(1024)
print(msg.decode('utf-8'))
count+=1
if __name__ == '__main__':
for i in range(100):
t=Thread(target=client,args=('127.0.0.1',8080))
t.start()
运行效果: