并发编程(四)——IO模型
1.为什么用IO模型
1.1多进程,多线程以及协程都有弊端
(1)单多进程:
能够有效利用多核优势,但是开启进程需要分配内存空间,占用内存,因此能够开启的进程有限,用进程池加以控制,因而并发量有限
(2)单多线程:
一个进程开启多个线程,线程并发量有限,同一时间只有一个线程占有CPU
(3)单线程实现并发:
协程:仅仅遇到IO阻塞时,进行切换切换的同时,不能实现并发
2.什么是IO模型
2.1 同步异步以及阻塞、非阻塞
同步异步:是提交任务后,是否停下来等待结果
同步:提交任务后,等到任务执行完,返回结果,才继续下一步任务(提交任务或者其他)
异步:提交任务后,不等待任务执行完,继续执行下一步(提交任务或者其他)
阻塞和非阻塞:
阻塞:遇到IO操作,程序停下来等待IO执行结果
非阻塞:遇到IO操作,不等待,继续执行
2.2:基于网络,我们常见有4种IO模型

每一次IO都要经历两个阶段即
第一阶段:waiting data 阶段,从不知哪里发来的数据到OS内存。准备阶段,应用程序不占用CPU,比较浪费,主要是针对这个阶段进行的优化
第二阶段:copy data 阶段:从系统内存把自己应用程序的数据copy到进程。此阶段耗时相当短暂
(1)阻塞IO
阻塞IO,就是在IO执行的两个阶段,即waiting data 和copy data 阶段都阻塞
服务端:
1 from socket import * 2 server = socket() 3 server.bind(('127.0.0.1',8080)) 4 server.listen(5) 5 6 conn,addr = server.accept()#阻塞的 7 while True: 8 data = conn.recv(1024)#阻塞的 9 if not data:break#linux 系统断开连接,接收空 10 conn.send(data.upper())#阻塞的,本质是阻塞,只是 发送端只关心将应用程序数据copy到内核
客户端:
1 from socket import * 2 client = socket() 3 client.connect(('127.0.0.1',8080)) 4 while True: 5 msg = input('>>>').strip() 6 if not msg:continue 7 client.send(bytes(msg,encoding='utf-8')) 8 data = client.recv(1024) 9 print(data.decode('utf-8'))
(2)非阻塞IO:优化waiting data 阶段,即:准备数据阶段不阻塞。
方式:通过socket的方法,设置socket的接口为非阻塞
1 客户端: 2 from socket import * 3 client = socket() 4 client.connect(('127.0.0.1',8080)) 5 while True: 6 msg = input('>>>').strip() 7 client.send(bytes(msg,encoding='utf-8')) 8 data = client.recv(1024) 9 print(data.decode('utf-8')) 10 11 服务端: 12 from socket import * 13 server = socket() 14 server.bind(('127.0.0.1',8080)) 15 server.listen(5) 16 server.setblocking(False)#设置socket的接口为非阻塞 17 conn_l = [] 18 del_l = [] 19 while True:#大幅度提高CPU的占用率,计算太多 20 try: 21 conn, addr = server.accept() 22 conn_l.append(conn) 23 except BlockingIOError: 24 print('没有数据') 25 for conn in conn_l:#遍历连接的列表,是否有通信过来 26 try: 27 data = conn.recv(1024) 28 if not data: 29 del_l.append(conn) # 针对Linux系统,断开连接后,发送空 30 continue 31 conn.send(data.upper()) 32 except BlockingIOError: 33 continue 34 except ConnectionRefusedError:#有连接断开时,抛出连接异常,关闭这个连接 35 conn.close() 36 del_l.append(conn) 37 for conn in del_l: 38 conn_l.remove(conn) 39 40 41 ''' 42 执行原理: 43 在accept的位置: 44 有连接过来,进行连接,并把这个连接加入到连接的列表 45 没有连接过来,到则阻塞,抛出BlockingIOError,进行recv的操作 46 在recv的位置,检测每个连接是否有通信连接 47 有通信,接收 48 没有,抛出BlockingIOError(是一种异常或者一种信号,表明此时没有数据过来) 49 50 51 弊端: 52 1.不断询问是否有连接过来,大幅度提高CPU的占用率,计算太多 53 2.任何一个任务的响应延迟增大了 54 '''
(3)IO多路复用
有三种模型:
1.select模型,检测多个套接字的IO行为,循环问,从头到尾,效率低
代替非阻塞IO的自己问询,由select模块进行检测是否有数据传来
2.poll模型:比select检测的套接字多,工作原理和select一样
3.epoll模型:哪个信息好了,返回信息。ngx检测多个套接字,绑定一个回调函数,基于Linux系统,Windows用不了
(4)异步IO
异步IO执行原理:向操作系统发出一个阻塞的信号后,由操作系统进行等待,应用程序做自己的任务,等待操作系统等待数据传递到操作系统的内存后,向应用程序发布一个信号,应用程序再从操作系统copy数据,省略了等待数据和的过程
3.用IO模型的益处

浙公网安备 33010602011771号