首页 > 其他分享 >【5.0】知识点小结(协程进阶)

【5.0】知识点小结(协程进阶)

时间:2023-06-27 09:35:44浏览次数:36  
标签:5.0 知识点 进阶 server client IO msg 链接 conn

【5.0】知识点小结(协程进阶)

【一】IO模型简介

  • 我们研究的 IO 都是基于网络 IO 的

  • Stevens在文章中一共比较了五种IO Model:

    • blocking IO
    • nonblocking IO
    • IO multiplexing
    • signal driven IO ---(忽略)
    • asynchronous IO 由signal driven IO(信号驱动IO)在实际中并不常用,所以主要介绍其余四种IO Model。
  • 当一个read操作发生时,该操作会经历两个阶段:

    • 1、等待数据准备 (Waiting for the data to be ready)
    • 2、将数据从内核拷贝到进程中(Copying the data from the kernel to the process)
  • 同步

    • 在同步模式下,一个组件必须等待另一个组件完成某个操作,然后再进行下一个操作。
  • 异步

    • 在异步模式下,组件之间互不干扰,它们可以独立地执行任务。当一个组件完成任务后,它可以直接通知另一个组件继续执行操作。
  • 阻塞

    • 在阻塞模式下,一个进程或线程在执行某个操作时,如果该操作没有完成,它将立即停止一切操作,并等待操作完成后再继续进行后续操作。
  • 非阻塞

    • 在非阻塞模式下,一个进程或线程在执行某个操作时,即使该操作没有完成,也会立即返回执行下一个操作,不会出现停滞等待的情况。 如果下一个操作需要依赖于上一个还未完成的操作,它将定期检查上一个操作是否完成,保证这些未完成的操作不会影响后续任务的执行。
  • 常见的网络IO

    • accept
    • recv
    • recvfrom

【1】阻塞IO模型

我们之前写的都是阻塞 IO 模型 (协程除外)

模型I

  • TCP/UDP协议模型

网络IO模型1

(1)TCP 经典IO

  • 客户端
# -*-coding: Utf-8 -*-
# @File : 01 阻塞IO模型 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)
  • 服务端
# -*-coding: Utf-8 -*-
# @File : 01 阻塞IO模型 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *

server = socket()

IP = '127.0.0.1'
PORT = 8082
server.bind((IP, PORT))

server.listen(5)

while True:
    conn, addr = server.accept()

    while True:
        try:
            msg_from_client = conn.recv(1024)

            if len(msg_from_client) == 0:
                break

            print(msg_from_client.decode('utf8'))

            conn.send(msg_from_client.upper())

        except  Exception as e:
            print(e)
            break

    conn.close()

问题:服务端只能接待一个客户端

(2)问题解决

在服务端开设多进程或多线程,进程池线程池,以此来解决IO问题

但是还没在根本上解决IO问题

因为在服务端 虽然解决了 请求数据这部分的IO 但是没有从根本上解决 向内核要数据和拷贝数据给客户端这个IO

【2】非阻塞IO模型

将所有的阻塞操作变为非阻塞

非阻塞IO模型

(1)TCP 非阻塞经典IO

  • 服务端
# -*-coding: Utf-8 -*-
# @File : 02 非阻塞IO模型 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26

from socket import *
import time

server = socket()

IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))

server.listen(5)

# 将所有的网络阻塞 变为 非阻塞
# BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。 ---- 表示服务端并没有立即建立链接,导致了资源无法接收
server.setblocking(False)

conn_list = []
del_conn_list = []
while True:
    try:
        conn, addr = server.accept()
        conn_list.append(conn)

    # 检测进程资源利用错误
    except BlockingIOError as e:
        print(f'当前无阻塞,可进行其他工作')
        for conn in conn_list:
            try:
                data_from_client = conn.recv(1024)  # 没有消息则报错
                if len(data_from_client) == 0:
                    # 如果接收到的消息为 0  则表示当前链接无效:关闭当前链接
                    conn.close()  # (客户端断开连接)
                    # 将无用的conn链接从链接列表中删除掉
                    del_conn_list.append(conn)  # (将无用的链接放到无用的链接列表里)
                    continue
                    
                # 链接对象正常:反馈消息给客户端
                conn.send(data_from_client.upper())
            # 检测进程资源利用错误
            except BlockingIOError as e:
                # 查看其他链接对象是否有消息发过来
                continue

            # 检测到客户端断开链接错误
            except ConnectionError as e:
                # 断开链接
                conn.close()
                # 将无效的链接添加到废弃列表里
                del_conn_list.append(conn)

        # 回收无用的链接对象
        for conn in del_conn_list:
            # 在有效列表内删除无效的链接对象
            conn_list.remove(conn)

        # 清空无效链接列表
        del_conn_list.clear()
  • 客户端
# -*-coding: Utf-8 -*-
# @File : 02 非阻塞IO模型 客户端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)

【3】总结

  • 虽然非阻塞 IO 看起来十分优秀
    • 但是该模型会长时间占用 CPU 而不处理数据(大部分资源都浪费在反复发起请求 请求数据上)
  • 实际应用中不会考虑使用非阻塞 IO 模型
    • 占用资源严重

【二】IO多路复用

【1】什么是IO多路复用

IO多路复用

  • 当监管的对象只有一个的时候,其实IO多路复用连阻塞IO的功能都比不上
  • 但是IO多路复用一次性可以监管多个对象
    • server = socket.socket()
    • conn, addr = socket.accept()
  • 这个监管机制是由操作系统本身就存在的(select)
  • 如果使用需要导入相关的模块(select模块)

【2】基于 select模块解决 IO 阻塞

  • 客户端
# -*-coding: Utf-8 -*-
# @File : 03 IO多路复用服务端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
from socket import *
import select

server = socket()

IP = '127.0.0.1'
PORT = 8083
server.bind((IP, PORT))
server.listen(5)

# 将阻塞模型变为非阻塞模型
server.setblocking(False)

# 将监管目标(谁来发起链接)添加到监管列表
read_list = [server]

while True:
    r_list, w_list, x_list = select.select(read_list, [], [])  # 检测server端
    # 返回值(server对象,[],[]) : ([<socket.socket fd=408, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8083)>], [], [])
    # 一旦有链接发起,直接拿到监管链接

    # 循环拿到每一个发起的链接对象
    for i in r_list:
        '''
        针对不同的对象进行分类处理
        '''

        # 如果是服务对象就将其添加到服务列表内,再次确认
        if i is server:
            # 拿到每一个链接对象
            conn, addr = i.accept()

            # 将链接对象添加到监管机制中
            read_list.append(conn)
        else:
            # 如果是链接请求就拿到客户端发起的数据
            msg_from_client = i.recv(1024)
            if len(res) == 0:
                # 关闭无效链接
                i.close()

                # 将无效链接从列表中移除
                read_list.remove(i)
                continue
            msg_from_client = msg_from_client.decode('utf-8')
            print(msg_from_client)

            # 反馈信息给客户端
            i.send(msg_from_client.upper())
  • 服务端
# -*-coding: Utf-8 -*-
# @File : 03 IO多路复用服务端 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26

from socket import *

client = socket()

IP = '127.0.0.1'
PORT = 8083
client.connect((IP, PORT))

while True:
    msg_to_server = 'hello world'
    msg_to_server = msg_to_server.encode('utf-8')

    client.send(msg_to_server)

    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('utf-8')

    print(msg_from_server)

【3】总结

  • 监管机制有很多
  • select机制
    • Windows/Mac 都有
  • poll机制
    • 只有Linux有
    • poll 和 select 机制都可以监管多个对象,但是 poll监管的对象更多

上述两个机制都不是很完美

当监管的对象特别多的时候,可能会出现机器大的延迟响应

  • epoll机制
    • 只在 Linux 机制有
    • 给每个监管对象都绑定了一个回调机制
    • 一旦有响应,回调机制会立刻发起提醒

针对不同的操作系统,需要考虑不同的监管机制,太过麻烦

因此有了一个新的模块(selectors模块)

根据平台的不同而自动选择不同的机制

【三】异步IO

异步IO 是所有模型中效率最高的,也是使用最广泛的

异步IO

由于涉及到调用操作系统底层,我们只能通过C语言代码才能模拟出效果

但是,有相关大佬已经帮我们将相关的方法进行了封装

  • 模块
    • asyncio 模块
  • 异步框架
    • sanic
    • tronado
    • twisted

【1】asyncio 模块

单线程实现并发效果

# -*-coding: Utf-8 -*-
# @File : 04 异步IO模块 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/6/26
import threading
import asyncio


@asyncio.coroutine
def hello():
    print(f'hello world statr:>>{threading.current_thread()}')

    yield from asyncio.sleep(1)  # 真正的 IO 操作 ---- 交互时操作 / 响应式操作

    print(f'hello world end:>>>{threading.current_thread()}')


loop = asyncio.get_event_loop()
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))

loop.close()

# 单线程实现并发效果

# hello world statr:>><_MainThread(MainThread, started 15588)>
# hello world statr:>><_MainThread(MainThread, started 15588)>
# hello world end:>>><_MainThread(MainThread, started 15588)>
# hello world end:>>><_MainThread(MainThread, started 15588)>

【四】IO总结

  • 四个IO模型对比

7

标签:5.0,知识点,进阶,server,client,IO,msg,链接,conn
From: https://www.cnblogs.com/dream-ze/p/17507779.html

相关文章

  • 强化学习从基础到进阶-常见问题和面试必知必答[6]:演员-评论员算法(advantage actor-cri
    强化学习从基础到进阶-常见问题和面试必知必答[6]:演员-评论员算法(advantageactor-critic,A2C),异步A2C、与生成对抗网络的联系等详解1.核心词汇优势演员-评论员(advantageactor-critic,A2C)算法:一种改进的演员-评论员(actor-critic)算法。异步优势演员-评论员(asynchronousadvanta......
  • F5iRules第二期iRules之进阶篇​
    F5iRules第二期iRules之进阶篇测试结果如下:......
  • 强化学习从基础到进阶-常见问题和面试必知必答[6]:演员-评论员算法(advantage actor-cri
    强化学习从基础到进阶-常见问题和面试必知必答[6]:演员-评论员算法(advantageactor-critic,A2C),异步A2C、与生成对抗网络的联系等详解1.核心词汇优势演员-评论员(advantageactor-critic,A2C)算法:一种改进的演员-评论员(actor-critic)算法。异步优势演员-评论员(asynchronousadvant......
  • 多项式(Ⅱ):进阶工业
    书接上回多项式(Ⅰ):基础工业。这部分主要写一下进阶的一些模板。多项式求逆多项式乘法逆给定一个多项式\(F(x)\),求出一个多项式\(G(x)\),满足\(F(x)*G(x)\equiv1(\bmod\x^n)\)。系数对\(998244353\)取模。我们先讨论比较简单的模数是\(998244353\)这样类型的。......
  • 2023年,软件测试趋于饱和,如何从功能测试进阶到自动化测试?
    功能测试转成自动化测试,答案就三个字:“靠学习”。学习自动化的方法无非是三种:一、靠培训在相对有氛围的学习环境中来学习自动化测试,这是一个较快学习的方法。二、靠自学自动化教程(下方有视频资源推荐)如果在职,不能全职学习,可以找一些自动化学习的视频,选择看视频学习,这也是一个不......
  • java知识点
           ......
  • 【3.0】知识点小结(线程相关)
    【3.0】知识点小结(线程相关)【一】什么是线程进程资源单位线程执行单位将操作系统比喻成大的工厂进程相当于工厂里面的车间线程相当于车间里面的流水线每一个进程必定自带一个线程进程:资源单位​ 起一个进程仅仅只是在内存空间中开辟出一块独立的空间......
  • 【4.0】知识点小结(线程进阶)
    【4.0】知识点小结(线程进阶)【一】什么是死锁与递归锁死锁是指两个或多个进程,在执行过程中,因争夺资源而造成了互相等待的一种现象。即两个或多个进程持有各自的锁并试图获取对方持有的锁,从而导致被阻塞,不能向前执行,最终形成僵局。在这种情况下,系统资源利用率极低,系统处于一种......
  • vue08进阶
    Vue基础一、创建一个Vue应用1、应用实例每个Vue应用都是通过createApp函数创建一个新的应用实例:import{createApp}from'vue'​constapp=createApp({  //根组件选项}) 2、根组件我们传入createApp的对象实际上是一个组件,每个应用都需要一个“根组件”,......
  • vue07进阶
    Vue开始什么是vue?Vue是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue都可以胜任。下面是一个最基本的示例:import{createApp}from'vu......