• 回顾线程协程,IO模型,阻塞非阻塞多路复用同步异步


    回顾

    线程队列

    1.Queue 与进程队列没啥区别

    2.LifoQueue 模拟栈这种结构 先进后出

    3.PriorityQueue 具备优先级的队列 比较越小的优先级越高

    无法存储自定义对象 因为自定义对象不具备 比较大小的方法 __lt__ __gt__

    解决方法 即给这个类 添加这两个方法的其中一个 运算符重载

    协程

    协程的原理是单线程实现并发

    协程,又叫微线程,是由应用程序自己来控制调度的多任务处理方式

    对比线程

    线程由操作系统来控制调度,创建线程也是需要占用系统资源的

    相比较协程呢,协程占用的资源小于线程,

    使用场景:

    1.一定是IO密集型

    2.当任务数量太大的时候,无法创建对应数量的线程

    性能对比:如果你的任务数量足够多的,就可以尽可能的利用CPU 直到超时为止

    注意:一定避免造成主线程阻塞 ,这会导致所有任务全部卡住

    协程不会造成,数据安全问题 因为只有一个执行单位

    Gevent

    1.封装了生成器

    2.可以检测IO操作并自动切换其它任务

    常用方法

    spawn() 提交任务

    join 等待该任务结束

    joinall 等待列表中的所有任务全部结束

    注意:使用该模块一定要先打补丁 ,mokey补丁 是把原本阻塞的代码替换成了非阻塞的代码

     

     

     

    回顾

    线程队列

    1.Queue 与进程队列没啥区别

    2.LifoQueue 模拟栈这种结构 先进后出

    3.PriorityQueue 具备优先级的队列 比较越小的优先级越高

    无法存储自定义对象 因为自定义对象不具备 比较大小的方法 __lt__ __gt__

    解决方法 即给这个类 添加这两个方法的其中一个 运算符重载

    协程

    协程的原理是单线程实现并发

    协程,又叫微线程,是由应用程序自己来控制调度的多任务处理方式

    对比线程

    线程由操作系统来控制调度,创建线程也是需要占用系统资源的

    相比较协程呢,协程占用的资源小于线程,

    使用场景:

    1.一定是IO密集型

    2.当任务数量太大的时候,无法创建对应数量的线程

    性能对比:如果你的任务数量足够多的,就可以尽可能的利用CPU 直到超时为止

    注意:一定避免造成主线程阻塞 ,这会导致所有任务全部卡住

    协程不会造成,数据安全问题 因为只有一个执行单位

    Gevent

    1.封装了生成器

    2.可以检测IO操作并自动切换其它任务

    常用方法

    spawn() 提交任务

    join 等待该任务结束

    joinall 等待列表中的所有任务全部结束

    注意:使用该模块一定要先打补丁 ,mokey补丁 是把原本阻塞的代码替换成了非阻塞的代码

     

     

     

    回顾

    线程队列

    1.Queue 与进程队列没啥区别

    2.LifoQueue 模拟栈这种结构 先进后出

    3.PriorityQueue 具备优先级的队列 比较越小的优先级越高

    无法存储自定义对象 因为自定义对象不具备 比较大小的方法 __lt__ __gt__

    解决方法 即给这个类 添加这两个方法的其中一个 运算符重载

    协程

    协程的原理是单线程实现并发

    协程,又叫微线程,是由应用程序自己来控制调度的多任务处理方式

    对比线程

    线程由操作系统来控制调度,创建线程也是需要占用系统资源的

    相比较协程呢,协程占用的资源小于线程,

    使用场景:

    1.一定是IO密集型

    2.当任务数量太大的时候,无法创建对应数量的线程

    性能对比:如果你的任务数量足够多的,就可以尽可能的利用CPU 直到超时为止

    注意:一定避免造成主线程阻塞 ,这会导致所有任务全部卡住

    协程不会造成,数据安全问题 因为只有一个执行单位

    Gevent

    1.封装了生成器

    2.可以检测IO操作并自动切换其它任务

    常用方法

    spawn() 提交任务

    join 等待该任务结束

    joinall 等待列表中的所有任务全部结束

    注意:使用该模块一定要先打补丁 ,mokey补丁 是把原本阻塞的代码替换成了非阻塞的代码

     

     

     

    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 设置为非阻塞

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

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

    多路复用IO模型

    属于事件驱动模型

     

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

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

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

    对比阻塞或非阻塞模型,增加了一个select,来帮我们检测socket的状态,从而避免了我们自己检测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)

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

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

     

     

    异步IO模型

    异步IO ==

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

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

     

    asyncio 3.4 出现

     

    信号驱动IO模型

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

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

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    学习笔记1:性能问题在于你想不到的任何地方!
    React Native控件之PullToRefreshViewAndroid下拉刷新组件解说(20)
    CSDN-markdown编辑器之导入导出文档功能
    无名管道与有名管道通讯编程
    LeetCode Remove Nth Node From End of List
    libpcap出错信息调试函数pcap_geterr, pcap_perror
    工作流学习——Activiti流程实例、任务管理四步曲
    B
    oracle学习笔记 oracle软件安装和数据库的创建
    路由器逆向分析------MIPS系统网络的配置(QEMU)
  • 原文地址:https://www.cnblogs.com/zrx19960128/p/11195159.html
Copyright © 2020-2023  润新知