并发编程-IO模型

IO模型

模型即解决某个问题的固定套路

I/O 指的是输入输出

IO的问题: 当我们要输入数据或是输出数据通常需要很长一段时间,当然是对于CPU而言

在等待输入的过程中,CPU就处于闲置状态 没事干! 造成了资源浪费

注意: IO其实有很多类型,例如,socket网络IO,内存到内存的copy,等待键盘输入,对比起来socket网络IO需要等待的时间是最长的,这也是咱们重点关注的地方,

学习IO模型要干什么? 就是在等待IO操作的过程中利用CPU,做别的事情

网络IO经历的步骤和过程

操作系统有两种状态:内核态 和 用户态 , 当操作系统需要控制硬件时,例如接收网卡上的数据,必须先转换到内核态,接收完数据后,要把数据从操作系统缓冲区,copy到应用程序的缓冲区,从内核态转为用户态;

涉及到的步骤

1.wait_data

2.copy_data

recv accept 需要经历 wait -> copy

send 只需要经历copy

阻塞IO模型

默认情况下 你写出TCP程序就是阻塞IO模型

该模型 提高效率方式,当你执行recv/accept 会进入wait_data的阶段,

1.你的进程会主动调用一个block指令,进程进入阻塞状态,同时让出CPU的执行权,操作系统就会将CPU分配给其它的任务,从而提高了CPU的利用率

2.当数据到达时,首先会从内核将数据copy到应用程序缓冲区,并且socket将唤醒处于自身的等待队列中的所有进程

注意:之前使用多线程 多进程 完成的并发 其实都是阻塞IO模型 每个线程在执行recv时,也会卡住

非阻塞IO模型

非阻塞IO模型与阻塞模型相反 ,在调用recv/accept 时都不会阻塞当前线程

使用方法: 将原本阻塞的socket 设置为非阻塞

该模型在没有数据到达时,会抛出异常,我们需要捕获异常,然后继续不断地询问系统内核直到数据到达为止
案例:

import socket
import time
s = socket.socket()
s.bind(("127.0.0.1",1688))
# 设置为非阻塞 模型
s.setblocking(False) #False表示不阻塞
s.listen()

# 保存所有的客户端socket
cs = []

# 用于保存需要发送的数据
msgs = []

while True:
    try:
        c,addr = s.accept()  # 完成三次握手
        print("来客了, xxx接客了!")
        cs.append(c)
    except BlockingIOError:
        print("还没有人来/. 好寂寞啊!")
        #  代码执行到这里则说明 没有连接需要处理
        #  就可以处理收发数据的任务
        for c in cs[:]:  # 专门处理接收数据
            try:
                data = c.recv(1024)
                if not data:
                   raise ConnectionResetError
                msgs.append((c,data.upper()))
                #c.send(data.upper()) # 不能直接发  当内核缓冲区满了 就会抛出异常 导致数据发不出去了
            except BlockingIOError:
                print("客官 你倒是说句话啊!")
                pass
            except ConnectionResetError:
                c.close()
                cs.remove(c)

        # 处理发送数据
        for i in msgs[:]:
            try:
                i[0].send(i[1])
                msgs.remove(i)
            except BlockingIOError:
                pass
            except ConnectionResetError:
                #关闭连接
                i[0].close()
                # 删除数据
                msgs.remove(i)
                # 删除连接
                cs.remove(i[0])

客户端

import socket
c = socket.socket()
c.connect(("127.0.0.1",1688))

while True:
    msg = input("").strip()
    if not msg:continue
    c.send(msg.encode("utf-8"))
    print(c.recv(1024).decode('utf-8'))

可以看出,该模型会大量的占用CPU资源做一些无效的循环, 效率低于阻塞IO

多路复用IO模型

属于事件驱动模型

多个socket使用同一套处理逻辑

如果将非阻塞IO 比喻是点餐的话,相当于你每次去前台,照着菜单挨个问个遍

而多路复用,相当于直接问前台那些菜做好了,前台会给你返回一个列表,里面就是已经做好的菜

对比阻塞或非阻塞模型,增加了一个select,来帮我们检测socket的状态,从而避免了我们自己检测socket带来的开销

select会把已经就绪的socket放入列表中,我们需要遍历列表,分别处理读写即可

多路复用中select的职责:

案例:

import socket
import time
import select
s = socket.socket()
s.bind(("127.0.0.1",1688))
# 设置为非阻塞 模型
s.setblocking(True) #在多路复用中  阻塞与非阻塞没有区别 因为select会阻塞直到有数据到达为止
s.listen()

# 待检测是否可读的列表
r_list = [s]
# 待检测是否可写的列表
w_list = []

# 待发送的数据
msgs = {}

print("开始检测了")
while True:
    read_ables, write_ables, _= select.select(r_list,w_list,[])
    print("检测出结果了!")
    # print(read_ables,"可以收数据了")
    # print(write_ables,"可以发数据了")
    # 处理可读 也就是接收数据的
    for obj in read_ables: # 拿出所有可以读数据的socket
        #有可能是服务器 有可能是客户端
        if s == obj: # 服务器
            print("来了一个客户端 要连接")
            client,addr = s.accept()
            r_list.append(client)  # 新的客户端也交给select检测了

        else:# 如果是客户端则执行recv 接收数据
            print("客户端发来一个数据")
            try:
                data = obj.recv(1024)
                if not data:raise ConnectionResetError
                print("有个客户端说:",data)
                # 将要发送数据的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)

客户端

import socket
c = socket.socket()
c.connect(("127.0.0.1",1688))

while True:
    msg = input("").strip()
    if not msg:continue
    c.send(msg.encode("utf-8"))
    print(c.recv(1024).decode('utf-8'))

多路复用对比非阻塞 ,多路复用可以极大降低CPU的占用率

注意:多路复用并不完美 因为本质上多个任务之间是串行的,如果某个任务耗时较长将导致其他的任务不能立即执行,多路复用最大的优势就是高并发

另外select 最大仅支持1024的socket同时处理,所以linux下的epoll才是最好的多路复用模型,详见:

https://www.cnblogs.com/yangyuanhu/p/11152914.html

异步IO模型

非阻塞IO不等于异步IO 因为copy的过程是一个同步任务 会卡主当前线程

而异步IO 是发起任务后 就可以继续执行其它任务,当数据copy到应用程序缓冲区完成后,才会给你的线程发送信号 或者执行回调

asyncio python3.4 出现 目前很多重要的第三方模块都不支持该模块,所以没有广泛使用,其内部也是使用了生成器!

信号驱动IO模型

简单的说就是 当某个事情发生后 会给你的线程发送一个信号,你的线程就可以去处理这个任务

不常用,原因是 socket的信号太多,处理起来非常繁琐

原文地址:https://www.cnblogs.com/yangyuanhu/p/11158791.html

时间: 2024-10-01 02:23:14

并发编程-IO模型的相关文章

python并发编程&IO模型

一 IO模型介绍 为了更好地了解IO模型,可先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别? 这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西. 这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(context)也不相同.所以,为了

15 并发编程-(IO模型)

一.IO模型介绍 1.阻塞与非阻塞指的是程序的两种运行状态 阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源 非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地,执行其他操作,力求尽可能多的占有CPU 2.同步与异步指的是提交任务的两种方式: 同步调用:提交完任务后,就在原地等待,直到任务运行完毕后,拿到任务的返回值,才继续执行下一行代码 异步调用:当进程执行到一个IO(等待外部数据)的时候,不需要等待,待数据接收

4.6 并发编程/IO模型

目录 4.6.1 背景概念 4.6.2 IO模型概念  4.6.2 IO模型分类 4.6.2.1 阻塞IO  (blocking IO) 4.6.2.2 非阻塞IO  (nonblocking IO) 4.6.2.3 多路复用IO  (IO multiplexing) 4.6.2.4 异步IO(asynchronous IO) 4.6.3 socketserver 模块   4.6.1 背景概念 4.6.2 IO模型概念  4.6.2 IO模型分类 4.6.2.1 阻塞IO  (blocking

并发编程 - IO模型 - 1.io模型/2.阻塞io/3.非阻塞io/4.多路复用io

1.io模型提交任务得方式: 同步:提交完任务,等结果,执行下一个任务 异步:提交完,接着执行,异步 + 回调 异步不等结果,提交完任务,任务执行完后,会自动触发回调函数同步不等于阻塞: 阻塞:遇到io,自己不处理,os会抢走cpu ,解决办法:监测到io,gevent切换到其他任务,类似欺骗os 非阻塞:cpu 运行 IO分类: 1.阻塞IO blocking IO 2.非阻塞IO nonblocking IO 3.IO多路复用 IO multiplexing 4.信号驱动IO signal

Python并发编程-IO模型-非阻塞IO实现SocketServer

Server.py import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.setblocking(False) #把socket中所有需要阻塞的方法都设为非阻塞IO, recv,accept, recvfrom sk.listen() conn_l = [] #保存所有来请求server端conn连接 del_conn = [] #用来存储所有已经与server端断开的conn while True: try: con

Python并发编程-IO模型-IO多路复用实现SocketServer

Server.py import select import socket sk = socket.socket() sk.bind(('127.0.0.1',8080)) sk.setblocking(False) sk.listen() read_lst = [sk] #select监听谁就放入list while True: #[sk,conn] r_lst,w_lst,x_lsx = select.select(read_lst,[],[]) # print('*******',r_ls

进程池与线程池、协程、协程实现TCP服务端并发、IO模型

进程池与线程池.协程.协程实现TCP服务端并发.IO模型 一.进程池与线程池 1.线程池 ''' 开进程开线程都需要消耗资源,只不过两者比较的情况下线程消耗的资源比较少 在计算机能够承受范围内最大限度的利用计算机 什么是池? 在保证计算机硬件安全的情况下最大限度的利用计算机 池其实是降低了程序的运行效率,但是保证了计算机硬件的安全 (硬件的发展跟不上软件的速度) ''' from concurrent.futures import ThreadPoolExecutor import time p

网络编程——IO模型介绍

网络编程--IO模型介绍 IO模型介绍 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞. 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答案都可能不同,比如wiki,就认为asynchronous IO和non-blocking IO是一个东西.这其实是因为不同的人的知识背景不同,并且在讨论这个问题的时候上下文(cont

Python并发编程-事件驱动模型

 一.事件驱动模型介绍                                                                                                        1.传统的编程模式  例如:线性模式大致流程 开始--->代码块A--->代码块B--->代码块C--->代码块D--->......--->结束 每一个代码块里是完成各种各样事情的代码,但编程者知道代码块A,B,C,D...的执行顺序,唯一能