并发编程-IO模型
IO模型
模型就是解决某个问题的固定套路
I/0指的是输出和输入
I/O操作产生的问题,就是当我们输入和输出数据通常需要很长一段时间(这对于CPU而言是如此),因此会造成资源浪费
注意IO其实有很多类型,例如,socket网络IO,内存到内存的copy,等待键盘输入的IO,对比起来网络IO 耗时最长,这也是我们需要重点关注的地方
而用IO要干什么? 就是在等待IO操作的过程中利用CPU做别的事情
网络IO经历的步骤和过程
操作系统有两种状态:内核态和用户态,当操作系统需要控制硬件时,例如接收网卡上的数据,必须先转换到内核态,接收完数据后,要把数据从操作系统缓冲区,copy到应用程序的缓冲区,从内核态转为用户态.
- 关于缓冲和缓存的简单介绍:
- 缓冲:将数据读入到内存所使用的空间 ---> 为了降低IO次数
- 缓存:从内存中读取数据,存放数据的空间叫做缓存 ---> 为了提高读取效率
- 步骤:
- wait_data : 网卡接收数据并读入到内核缓冲区
- copy_data : 把数据从内核缓冲区copy到应用层缓冲区
- 过程:
- recv accept 需要经历:wari--->copy
- send 只需要经历copy(等待时间很短,可以忽略不计)
阻塞IO模型
默认情况下,TCP程序就是阻塞IO模型
运行流程:
- 当执行recv/accept,会进入wait_data的阶段,
- 进程会主动调用一个block指令,使进程进入阻塞状态,同时让出CPU的执行权,操作系统就会将CPU分配给其他的任务,从而提高了CPU的利用率
- 当数据到达时,首先会从内核将数据copy到应用程序缓冲区,并且socket将会唤醒出于自身的等待队列中的所有进程之前使用多线程/多进程完成的并发,其实都是阻塞IO模型,每个线程在执行recv时,也会卡主.
非阻塞IO模型
非阻塞IO模型与阻塞IO模型相反,在调用recv/accept时都不会阻塞当前线程
使用方法: 将原本阻塞的socket设置为非阻塞
该模型在没有数据到达时,会抛出异常,我们需要捕获异常,然后不断地询问系统内核知道数据到达为止,可看出,该模型会大量的占用CPU资源做一些无效的循环,效率低于阻塞IO
多路复用IO模型(事件驱动模型)
多个socket使用同一套处理逻辑
如果将非阻塞IO比喻是点餐的话,相当于你每次去前台,照着菜单挨个问个遍
而多路复用,直接为前台那些菜做好了,前台会给你返回一个列表,里面就是已经做好的菜;
对比阻塞或者非阻塞模型,增加一个select,来帮我们检测socket的状态,从而避免了我们自己检测socket带来的开销,select会把已经就是的放入列表中,我们遍历列表,分别处理读写即可.
案例:
import socket,select
server = socket.socket()
server.bind(('127.0.0.1',8080))
# server.setblocking(True) # 在多路复用中,阻塞与非阻塞没有区别,因为select 会阻塞直到有数据打到为止.
server.listen()
# 待检测是否可读的列表
r_list = [server]
# 待检测是否可写的列表
w_list = []
# 待发送的数据
msgs = {}
print('开始检测了')
while True:
read_ables,write_ables,_ = select.select(r_list,w_list,[])
# 处理可读,也就是接收数据的
for obj in read_ables: # 拿出所有可以读数据的socket,里面有可能是服务器,有可能是客户端
# 服务器
if obj == server:
print('来了一个客户端')
conn,address = server.accept()
r_list.append(conn) # 新的客户端也交给select检测
else: # 如果是客户端,则执行recv 接收数据
print('客户端有数据')
try:
data = obj.recv(1024)
if not data : raise ConnectionResetError
# 将要发送的数据socket加入到列表中让select检测
w_list.append(obj)
# 将要发送的数据已经socket对象丢到容器中
if obj in msgs: # 由于容器是一个列表,所以需要先判断是否存在了列表
msgs[obj].append(data)
else:
msgs[obj] = [data]
except ConnectionResetError:
obj.close()
r_list.remove(obj)
# 处理可写的,也就是send发送数据
for obj in write_ables:
msg_list = msgs.get(obj)
if msg_list:
# 遍历发送所有数据
for m in msg_list:
try:
obj.send(m.upper())
except ConnectionResetError:
obj.close()
w_list.remove(obj)
break
# 数据从容器中删除
msgs.pop(obj)
# 将这个socket从w_list中删除
w_list.remove(obj)
[/code]
多路复用对比非阻塞,多路复用可以极大降低CPU的占用率
注意:多路复用并不完美,因为本质上多个任务之间是串行的,如果某个任务耗时较长时,将导致其他的任务不能立即执行,多路复用最大的优势就是高并发
转载于:https://www.cnblogs.com/raynduan/p/11286472.html
- IO复用、多进程和多线程三种并发编程模型
- python并发编程:IO模型
- 并发编程 - IO模型 - 1.io模型/2.阻塞io/3.非阻塞io/4.多路复用io
- python并发编程之IO模型
- Linux网络编程服务器模型选择之IO复用循环并发服务器
- 并发编程 - io模型 - 总结
- IO复用、多进程和多线程三种并发编程模型比较
- python并发编程:IO模型基础
- python并发编程之IO模型
- 服务器编程模型 循环服务器->IO复用循环服务器 并发服务器->高性能并发服务器
- python并发编程之IO模型,
- 并发编程之IO模型
- IO复用、多进程和多线程三种并发编程模型
- Linux网络编程服务器模型选择之IO复用循环并发服务器
- 网络编程与并发-IO模型
- IO复用、多进程和多线程三种并发编程模型
- 并发编程模型和访问控制
- 并发编程模型
- 漫谈并发编程:Actor模型
- C++并发编程实战(读书笔记)——C++内存模型不好理解;无锁数据结构?但是等待不就是被锁住了吗??