协程
协程,自己控制cpu在多任务中切换,即单线程实现并发任务。单线程无法实现并行,但可实现并发。
协程:是一种用户态的轻量级线程,即协程是由用户程序自己控制调度。
优点:
切换开销更小
单线程内可以实现并发效果,最大限度利用cpu
缺点:
协程无法利用多核
协程为单个线程,一旦阻塞则阻塞整个线程
总结:
必须在只有一个单线程里实现并发
修改数据不用加锁
用户程序里自己保存多个控制流的上下文栈
yeild实现纯计算对比:
#并发 def producer(): g=consumer() next(g) for i in range(10000): print("producer") g.send(i) def consumer(): while True: res=yield print("consumer") import time start_time=time.time() producer() print(time.time()-start_time)
#串行 def producer(): res=[] for i in range(10000): print("producer") res.append(i) def consumer(res): pass import time start_time=time.time() producer() print(time.time()-start_time)
协程,降低I/O阻塞,提高利用率
greenlet模块,实现多模块之间切换,多任务I/O密集型使用。
#pip3 install greenlet from greenlet import greenlet def eat(name): print("%s eat 1" % name) g2.switch("ya") print("%s eat 2" % name) g2.switch() def play(name): print("%s play 1" % name) g1.switch() print("%s play 2" % name) g1.switch() g1=greenlet(eat) g2=greenlet(play) g1.switch("ya")
gevent模块
默认为异步提交
import gevent,time from gevent import monkey monkey.patch_all()#监控所有I/O操作,否则只能识别sleep() def eat(name): print("%s eat 1" % name) gevent.sleep(3) print("%s eat 2" % name) def play(name): print("%s play 1" % name) gevent.sleep(5) print("%s play 2" % name) start_time=time.time() g1=gevent.spawn(eat,"ya") g2=gevent.spawn(play,"ya") g1.join() g2.join() print(time.time()-start_time)
gevent模块异步提交任务,join(),保证主线程等待任务执行完毕
也可以用joinall()方法
gevent.joinall([g1,g2])
gevent协程实现套接字
server:
from socket import * from gevent import monkey,spawn;monkey.patch_all() def talk(conn): print(conn) while True: try: print("recv") data=conn.recv(1024) if not data: break conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(ip,port): server=socket(AF_INET,SOCK_STREAM) # server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) server.bind((ip,port)) server.listen(5) while True: conn,addr=server.accept() spawn(talk,conn) server.close() if __name__ == "__main__": g=spawn(server,"127.0.0.1",8080) g.join()
client:
from threading import Thread,currentThread from socket import * def client(): client=socket(AF_INET,SOCK_STREAM) client.connect(("127.0.0.1",8080)) while True: client.send(("%s say hello" % currentThread().getName()).encode("utf-8")) data=client.recv(1024) print(data.decode("utf-8")) if __name__=="__main__": for i in range(500): t=Thread(target=client) t.start()