协程,IO模式
1、协程(别人的模块,达到单线程并发效果)
程序的运行状态:
阻塞:
IO阻塞
非阻塞:
运行
就绪
单线程实现并发:
在应用程序里控制多个任务的切换+保存状态
可以把IO减下来,但是不可能降到无
优点:
应用程序级别速度要远远高于操作系统的切换
缺点:
多个任务一旦有一个阻塞没有切,整个县城都堵塞在原地
该线程内的其他的任务都不能执行
一旦引入协程,就需要检测单线程下素有的IO行为,
实现遇到IO就切换,少一个都不行,因为一旦一个任务阻塞,整个线程就 阻塞,其他的任务即便是可以计算,但是也无法运行。
2、用协程的目的
想要在单线程下实现并发
并发指的是多个任务看起来是同时运行的
并发=切换+保存状态
线程在进程内,进程在操作系统内,一切由操作系统控制
自己设置python程序让线程形成切换,不用操作系统控制。
协程只有单线程下遇到IO切换才会提成效率,操作系统相对与协程要更慢点
import time def func1(): for i in range(1000000): i+1 def func2(): for i in range(1000000): i+1 start = time.time() func1() func2() stop=time.time() print(stop-start) # 基于yield并发 import time def func1(): while True: print("func1") yield def func2(): g=func1() for i in range(1000000): print("func2") i+1 time.sleep(3) next(g) start=time.time() func2() stop=time.time() print(stop-start)
from gevent import monkey,spawn;monkey.patch_all()#monkey.patch_all()补丁 import time def eat(name): print("%s eat 1" %name) time.sleep(3) print("%s eat 2" %name) def play(name): print("%s play 1" %name) time.sleep(1) print("%s play 2" %name) start=time.time() g1=spawn(eat,"yf")#自动运行任务 模块 g2=spawn(play,"fxc") g1.join()#通过补丁让join识别其他IO g2.join() print(time.time() - start) print(g1) print(g2) from gevent import monkey,spawn;monkey.patch_all() from threading import current_thread import time def eat(name): print("%s eat 1" %current_thread().name)#都是一个线程,前面有dummy假标明 time.sleep(3) print("%s eat 2" %current_thread().name) def play(name): print("%s play 1" %current_thread().name) time.sleep(1) print("%s play 2" %current_thread().name) start=time.time() g1=spawn(eat,"yf") g2=spawn(play,"fxc") g1.join() g2.join() print(time.time() - start) print(g1) print(g2)
并发的套接字通信:
from gevent import spawn,monkey;monkey.patch_all() from socket import * from threading import Thread def talk(conn): while True: try: data = conn.recv(1024) if len(data)==0:break conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(ip,port,backlog=5): server = socket(AF_INET,SOCK_STREAM) server.bind((ip,port)) server.listen(backlog) print("starting..") while True: conn,addr=server.accept() spawn(talk,conn) if __name__ == '__main__': g=spawn(server,"127.0.0.1",8080) g.join()
from threading import Thread,current_thread from socket import * def task(): client = socket(AF_INET,SOCK_STREAM) client.connect(("127.0.0.1",8080)) while True: msg="%s say hello" %current_thread().name client.send(msg.encode("utf-8")) data=client.recv(1024) print(data.decode("utf-8")) if __name__ == '__main__': for i in range(500): t=Thread(target=task) t.start()
网络IO:
recvfrom:#等待客户链接请求
wait data:等待客户端产生数据——》客户端OS--》网络--》服务端操作系统缓存
copy data:由本地操作系统缓存中的数据拷贝到应用程序的内存中
send:
copy data
conn.recv(1024) ==>OS
from socket import * import time server = socket() server.bind(("127.0.0.1",8080)) server.listen(5) server.setblocking(False)#是否有链接过来 conn_l=[] while True: try: print("总连接数[%s]" %len(conn_l)) conn,addr=server.accept() conn_l.append(conn) except BlockingIOError: del_l=[] for conn in conn_l: try: data=conn.recv(1024) if len(data)==0: del_l.append(conn) continue conn.send(data.upper()) except BlockingIOError: pass except ConnectionResetError: del_l.append(conn) for conn in del_l: conn_l.remove(conn)
from socket import * import os client=socket(AF_INET,SOCK_STREAM) client.connect(("127.0.0.1",8080)) while True: msg="%s say hello" %os.getpid() client.send(msg.encode("utf-8")) data=client.recv(1024) print(data.dacoda("utf-8"))