同步與異步的性能區別
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()