python协程函数 python协程用法( 二 )


(2)poll
不同于select使用三个位图来表示三个fdset的方式,poll使用一个pollfd的指针实现 。
pollfd结构包含了要监视的event和发生的event,不再使用select"参数-值"传递的方式 。同时pollfd并没有最大数量限制(但是数量过大后性能也会下降) 。和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符 。
从上面看,select和poll都需要在返回后通过遍历文件描述符来获取已经就绪的socket 。事实上同时连接的大量客户端在同一时刻可能只有很少的处于就绪的状态,因此随着监视的描述符数量的增长,其效率也会线性下降
(3)epoll
epoll是在2.6内核中提出的,是之前的select和poll的增强版本 。相对于select和poll来说,epoll更加领灵活,没有描述符限制 。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次 。
Python协程之asyncio asyncio 是 Python 中的异步IO库,用来编写并发协程,适用于IO阻塞且需要大量并发的场景,例如爬虫、文件读写 。
asyncio 在 Python3.4 被引入,经过几个版本的迭代,特性、语法糖均有了不同程度的改进,这也使得不同版本的 Python 在 asyncio 的用法上各不相同,显得有些杂乱,以前使用的时候也是本着能用就行的原则 , 在写法上走了一些弯路 , 现在对 Python3.7+ 和 Python3.6 中 asyncio 的用法做一个梳理,以便以后能更好的使用 。
协程,又称微线程,它不被操作系统内核所管理,而完全是由程序控制,协程切换花销小,因而有更高的性能 。
协程可以比作子程序,不同的是,执行过程中协程可以挂起当前状态,转而执行其他协程,在适当的时候返回来接着执行,协程间的切换不需要涉及任何系统调用或任何阻塞调用 , 完全由协程调度器进行调度 。
Python 中以 asyncio 为依赖,使用 async/await 语法进行协程的创建和使用,如下 async 语法创建一个协程函数:
在协程中除了普通函数的功能外最主要的作用就是:使用 await 语法等待另一个协程结束,这将挂起当前协程,直到另一个协程产生结果再继续执行:
asyncio.sleep()是 asyncio 包内置的协程函数 , 这里模拟耗时的IO操作,上面这个协程执行到这一句会挂起当前协程而去执行其他协程,直到sleep结束,当有多个协程任务时,这种切换会让它们的IO操作并行处理 。
注意,执行一个协程函数并不会真正的运行它 , 而是会返回一个协程对象,要使协程真正的运行,需要将它们加入到事件循环中运行 , 官方建议 asyncio 程序应当有一个主入口协程,用来管理所有其他的协程任务:
在 Python3.7+ 中,运行这个 asyncio 程序只需要一句: asyncio.run(main()),而在 Python3.6 中 , 需要手动获取事件循环并加入协程任务:
事件循环就是一个循环队列,对其中的协程进行调度执行,当把一个协程加入循环 , 这个协程创建的其他协程都会自动加入到当前事件循环中 。
其实协程对象也不是直接运行,而是被封装成一个个待执行的 Task ,大多数情况下 asyncio 会帮我们进行封装,我们也可以提前自行封装 Task 来获得对协程更多的控制权,注意,封装 Task 需要当前线程有正在运行的事件循环,否则将引 RuntimeError,这也就是官方建议使用主入口协程的原因,如果在主入口协程之外创建任务就需要先手动获取事件循环然后使用底层方法loop.create_task() ,而在主入口协程之内是一定有正在运行的循环的 。任务创建后便有了状态,可以查看运行情况,查看结果,取消任务等:

推荐阅读