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()
运行效果: