Python异步编程原理篇之协程的IO
协程的IO
asyncio 作为实现异步编程的库,任务执行中遇到系统IO的时能够自动切换到其他任务。协程使用的IO模型是IO多路复用。在 asyncio 低阶API 一篇中提到过 “以Linux系统为例,IO模型有阻塞,非阻塞,IO多路复用等。asyncio 常用的是IO多路复用模型的epool和 kqueue”。本篇就介绍一下IO多路复用技术以及操作系统的IO,为后续内容做一个铺垫。
什么是IO
根据冯.诺依曼结构,它将计算机分成分为5个部分:运算器、控制器、存储器、输入设备、输出设备。
涉及计算机核心与其他设备间数据迁移的过程就是IO

常见的IO包括:文件的读写、网络请求。
以文件读写为例,一个应用程序读一个文件。一个应用程序就是一个进程,操作系统为每一个进程分配的内存分为两个部分,分别是用户空间和内核空间。以32位系统为例,用户空间分配3GB,内核空间分为1GB。IO操作因为都是和硬件设备交互,所以不能让用户进程直接操作,而是需要进程调用操作提供提供的API来完成。

应用程序读一个文件的流程是:
- 应用程序调用系统提供读文件的命令
- 系统将磁盘中文件内容读取到内核空间
- 系统将文件内容从内核空间拷贝的用户空间
- 应用程序读取用户空间中的文件内容
从文件IO总结IO的基本流程为:

总结来看,IO操作的基本流程是:
- 应用程序发起IO调用
- 操作系统完成IO操作
阻塞IO模型
阻塞IO模型就是应用程序发起IO调用之后一直阻塞等待,一直等到数据从内核空间拷贝用户空间,此次调用才算完成。
流程图如下:

存在问题:
如果内核数据一直没准备好,那用户进程将一直阻塞,CPU空转而浪费时间。并发大的情况下将导致进程数量变大,限制并发数量。
非阻塞IO模型
应用程序发起IO调用,如果内核空间数据还没读取完成,可以先返回错误信息给用户进程,让它不需要等待,而是通过轮询的方式再来请求。这就是非阻塞IO,流程图如下:

非阻塞IO的流程如下:
- 应用进程向操作系统内核,发起读取数据。
- 操作系统内核数据没有准备好,立即返回错误码。
- 应用程序轮询调用,继续向操作系统内核发起读取数据。
- 操作系统内核空间读取数据完成,从内核缓冲区拷贝到用户空间。
- 完成调用,返回成功提示。
存在问题:
它相对于阻塞IO,虽然大幅提升了性能,但是它依然存在性能问题,即频繁的轮询,导致频繁的系统调用,同样会消耗大量的CPU资源。
IO多路复用模型
非阻塞IO的问题
非阻塞IO模型下并发情况下应用程序可能会发送上千次请求,如果每一次请求的IO都需要轮询获取结果,那么应用就需要创建上千个线程去轮询监听数据是否拷贝完成。

这么多的线程不断调用系统函数 recvfrom 请求数据,首先服务器不能支持这么多请求,其次这种方式太浪费资源了,线程是我们操作系统的宝贵资源,大量的线程用来去读取数据了,那么就意味着能做其它事情的线程就会少。如何解决这个问题呢?使用IO多路复用可以将轮询监听的线程降低到1个。
IO多路复用介绍
IO多路复用的原理:
可以由一个线程监控多个网络请求,当有数据准备好之后再通知对应的线程去读取数据。这样就可以只需要一个线程完成数据是否就绪状态的查询。通过复用一个轮询的线程节省出大量的线程资源出来,这个就是IO复用模型的思路。
IO多路复用的流程:
- 应用程序调用IO请求返回一个文件描述符
- IO多路复用的函数(select、poll、epoll)同时监控多个文件描述符
- 当某一个文件描述符的状态变成就绪时,IO多路复用函数通知对应应用程序
- 应用程序读取文件,数据从内核空间拷贝的用户空间,完成数据IO

IO多路复用使用的函数有三种,分别是:select、poll、epoll。三者在实现上有一些区别。IO多路复用实现的核心思想是监听文件描述符fd的状态,当fd状态就绪时通知对应的应用读取数据。
select
应用进程通过调用select函数,可以同时监控多个文件描述符。在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。
select缺点:
- 监听的IO最大连接数有限,在Linux系统上一般为1024。
- select函数是通过遍历fdset,找到就绪的描述符fd。遍历的时间性能消耗较大
poll
由于select存在连接数限制,所以后来又提出了poll。poll模型里面通过使用链表的形式来保存自己监控的fd信息,连接数限制问题。
缺点:
select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。
epoll
epoll并不是像select一样去遍历事件列表逐个轮询的监控fd的事件状态,而是事先就建立了fd与之对应的回调函数,当事件激活后主动回调将fd加入到就绪链表中,这也就避免了遍历事件列表的这个操作。
这里去掉了遍历文件描述符的低性能操作,而是采用监听事件回调的的机制。这就是epoll的亮点。
小结
需要注意的是IO多路复用也是阻塞的IO,只不过它能并发处理的IO效率更高。
信号驱动模型
信号驱动IO不再用主动询问的方式去确认数据是否就绪,而是向内核发送一个信号(调用sigaction的时候建立一个SIGIO的信号),然后应用用户进程可以去做别的事,不用阻塞。当内核数据准备好后,再通过SIGIO信号通知应用进程,数据准备好后的可读状态。应用用户进程收到信号之后,立即调用recvfrom,去读取数据。

信号驱动IO模型,在应用进程发出信号后,是立即返回的,不会阻塞进程。它已经有异步操作的感觉了。但是数据复制到应用缓冲的时候,应用进程还是阻塞的。
回过头来看下,不管是非阻塞IO、IO多路复用还是信号驱动,在数据从内核复制到应用缓冲的时候,都是阻塞的。
异步IO模型
非阻塞IO、IO多路复用还是信号驱动在数据从内核复制到应用缓冲的时候,都是阻塞的,因此都不是真正的异步。
异步IO实现了IO全流程的非阻塞,就是应用进程发出系统调用后,是立即返回的,但是立即返回的不是处理结果,而是表示提交成功类似的意思。等内核数据准备好,将数据拷贝到用户进程缓冲区,发送信号通知用户进程IO操作执行完毕。

异步IO的原理很简单,只需要向内核发送一次请求,就可以完成数据状态询问和数据拷贝的所有操作,并且不用阻塞等待结果。
同步、异步、阻塞、非阻塞总结
相关术语:
- 同步阻塞(blocking-IO)简称BIO
- 同步非阻塞(non-blocking-IO)简称NIO
- 异步非阻塞(asynchronous-non-blocking-IO)简称AIO

参考文章:
https://zhuanlan.zhihu.com/p/439770090
Python异步编程原理篇之协程的IO的更多相关文章
- Python【第十篇】协程、异步IO
大纲 Gevent协程 阻塞IO和非阻塞IO.同步IO和异步IO的区别 事件驱动.IO多路复用(select/poll/epoll) 1.协程 1.1协程的概念 协程,又称微线程,纤程.英文名Coro ...
- python 并发编程 基于gevent模块 协程池 实现并发的套接字通信
基于协程池 实现并发的套接字通信 客户端: from socket import * client = socket(AF_INET, SOCK_STREAM) client.connect(('12 ...
- 这篇文章讲得精彩-深入理解 Python 异步编程(上)!
可惜,二和三现在还没有出来~ ~~~~~~~~~~~~~~~~~~~~~~~~~ http://python.jobbole.com/88291/ ~~~~~~~~~~~~~~~~~~~~~~~~~~ ...
- 深入理解 Python 异步编程(上)
http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知 ...
- python 异步编程
Python 3.5 协程究竟是个啥 Yushneng · Mar 10th, 2016 原文链接 : How the heck does async/await work in Python 3.5 ...
- 最新Python异步编程详解
我们都知道对于I/O相关的程序来说,异步编程可以大幅度的提高系统的吞吐量,因为在某个I/O操作的读写过程中,系统可以先去处理其它的操作(通常是其它的I/O操作),那么Python中是如何实现异步编程的 ...
- python异步编程 (转载)
Python Async/Await入门指南 转自:https://zhuanlan.zhihu.com/p/27258289 本文将会讲述Python 3.5之后出现的async/await的使 ...
- JavaScript异步编程原理
众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...
- Python之线程、进程和协程
python之线程.进程和协程 目录: 引言 一.线程 1.1 普通的多线程 1.2 自定义线程类 1.3 线程锁 1.3.1 未使用锁 1.3.2 普通锁Lock和RLock 1.3.3 信号量(S ...
- 深入理解Python异步编程(上)
本文代码整理自:深入理解Python异步编程(上) 参考:A Web Crawler With asyncio Coroutines 一.同步阻塞方式 import socket def blocki ...
随机推荐
- 2023-12-20:用go语言,给定一个数组arr,长度为n,在其中要选两个不相交的子数组。 两个子数组的累加和都要是T,返回所有满足情况中,两个子数组长度之和最小是多少? 如果没有有效方法,返回-
2023-12-20:用go语言,给定一个数组arr,长度为n,在其中要选两个不相交的子数组. 两个子数组的累加和都要是T,返回所有满足情况中,两个子数组长度之和最小是多少? 如果没有有效方法,返回- ...
- 生成式AI:未来的发展方向是什么?
生成式AI的问世标志着人工智能领域迎来了一个全新时代的开启.今年,ChatGPT的面世引起了广泛的热议和关注,许多人认为这标志着人工智能领域进入了一个大规模探索的时代.然而,事实上,这只是生成式AI发 ...
- 浅谈webview与js交互
浅谈下webview与js的交互,项目中因为需要获取页面中的许多属性,所以用到了这些,让后端在方法中传了过来. 下面的demo是本地的html代码,具体可以仿写,都差不多,注重思想. 1:项目目录 2 ...
- 从零玩转Docker之docker-compose-azdocker-compose
title: 从零玩转Docker之docker-compose date: 2023-04-04 17:39:40.699 updated: 2023-04-04 17:52:15.329 url: ...
- 解决方案 | VS2022 + AutoCAD2024 + ObjectARX2024环境搭建过程
一.准备工具 1.vs2022 自行网络搜索,各种版本均可(比如专业版.社区版),注意使用社区版必须使用最新版,目前是17.8版本,否则最终会无法使用样板. 2.cad2024 自行网络搜索 3.Ob ...
- Baidu Comate实践指南,惊艳了我...
1 啥是Baidu Comate Comate是百度开发的编程大模型工具,它基于文心大模型,结合百度积累多年的编程现场大数据和外部优秀开源数据,为我们生成更符合实际研发场景的优质代码:它能提升编码效率 ...
- 详解ZooKeeper在微服务注册中心的应用
本文分享自华为云社区<SpringCloud ZooKeeper 详解,以及与Go.Rust等非Java服务的集成>,作者: 张俭. ZooKeeper,是一个开源的分布式协调服务,不仅支 ...
- 【玩转鲲鹏DevKit系列】何如快速迁移有源码应用
本文分享自华为云社区<[玩转鲲鹏DevKit系列]何如快速迁移有源码应用>,作者:华为云社区精选 . 源码(也称源程序)是程序员编写的计算机程序的文本形式,不同的编程语言有不同的语法和规则 ...
- 【API进阶之路】因为不会创建云服务器,我被实习生摆了一道
摘要:9个步骤通过API创建云服务器. 事情是这样的,公司成立了一个新项目,要给项目建官网,并搭建一个web服务器.按照我的习惯,搭建一个本地虚拟机就好了,部署简单方便还好配置,云服务器嘛,写了10年 ...
- EDS从小白到专家丨生态产业链高效协同的一计良策
本文分享自华为云社区<[EDS从小白到专家]第3期:生态产业链高效协同的一计良策>,作者:开天aPaaS小助手 . 号外! 华为将于2023年9月20-22日,在上海世博展览馆和上海世博中 ...