同步与异步的性能区别
1.
#coding:utf-8 import gevent def task(pid): """ Some non-deterministic task """ gevent.sleep(0.5) #起到切换的作用 print('Task %s done' % pid) def synchronous(): for i in range(1,10): task(i) def asynchronous(): threads = [gevent.spawn(task, i) for i in range(10)] gevent.joinall(threads) #等待所以操作都执行完毕 print('Synchronous:') synchronous() #同步 print('Asynchronous:') asynchronous()#异步 这里会按照sleep 设置来执行
2.Python通过yield
提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。1.可用使用gevent.sleep()调整执行顺序也可以2.使用monkey patch来修改标准库,不改原始代码的方式调整。
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:
遇到IO阻塞时会自动切换任务
#coding:utf-8 from urllib import urlopen import gevent from gevent import monkey;monkey.patch_all() #修改标准库,使IO操作时,还会继续执行其他的协程 def t(n): print n url=urlopen(n) #遇到IO操作都会自动执行其他协程 urll=url.read() print 'len%s,url%s'%(len(urll),n) gevent.joinall([ gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9342446.html'), gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9329362.html'), gevent.spawn(t,'https://www.cnblogs.com/iexperience/p/9329332.html'), ])
结果:
https://www.cnblogs.com/iexperience/p/9342446.html
https://www.cnblogs.com/iexperience/p/9329362.html
https://www.cnblogs.com/iexperience/p/9329332.html
len10649,urlhttps://www.cnblogs.com/iexperience/p/9342446.html
len12980,urlhttps://www.cnblogs.com/iexperience/p/9329362.html
len7646,urlhttps://www.cnblogs.com/iexperience/p/9329332.html
从结果看,3个网络操作是并发执行的,而且结束顺序不同,但只有一个线程。
要让greenlet交替运行,可以通过gevent.sleep()
交出控制权,像开始的实例1中的异步一样:
参考:https://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001407503089986d175822da68d4d6685fbe849a0e0ca35000
通过gevent来实现单线程下的多socket并发
server 端,采用gevent协程
import sys import socket import time import gevent from gevent import socket,monkey monkey.patch_all() def server(port): s = socket.socket() s.bind(('0.0.0.0', port)) s.listen(500) while True: cli, addr = s.accept() gevent.spawn(handle_request, cli) #gevent.spwan调用handle参数并传参 def handle_request(conn): try: while True: data = conn.recv(1024) print("recv:", data) conn.send(data) if not data: conn.shutdown(socket.SHUT_WR) except Exception as ex: print(ex) finally: conn.close() if __name__ == '__main__': server(8001)
client端
单线程的客户端
import socket HOST = 'localhost' # The remote host PORT = 8001 # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) while True: msg = bytes(input(">>:"),encoding="utf8") s.sendall(msg) data = s.recv(1024) #print(data) print('Received', repr(data)) s.close()
多线程客户端去请求
import socket import threading def sock_conn(): client = socket.socket() client.connect(("localhost",8001)) count = 0 while True: #msg = input(">>:").strip() #if len(msg) == 0:continue client.send( ("hello %s" %count).encode("utf-8")) data = client.recv(1024) print("[%s]recv from server:" % threading.get_ident(),data.decode()) #结果 count +=1 client.close() for i in range(100): t = threading.Thread(target=sock_conn) t.start()