Python 异步编程笔记:asyncio
个人笔记,不保证正确。
虽然说看到很多人不看好 asyncio,但是这个东西还是必须学的。。
基于协程的异步,在很多语言中都有,学会了 Python 的,就一通百通。
一、生成器 generator
Python 的 asyncio 是通过 generator 实现的,要学习 async,先得复习下 generator.
1. yield
众所周知,yield 是用于定义 generator 函数的关键字,调用该函数,会返回一个 generator
>>> def f():
... yield 1
... yield 2
...
>>> f() # 返回的是 generator
<generator object f at 0x7f672c460570>
>>> g = f()
>>> next(g) # 通过 next 方法从 generator 获取值
1
>>> g.__next__() # next 方法实际是调用了 generator 的 __next__ 方法
2
>>> next(g) # 生成器运行结束,产生一个 StopIteration 的 exception
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
每次调用 next,generator 都只会运行到下一个 yield 关键字所在行,返回 yield 右侧的对象,然后暂停在该处,等待下一次 next 调用。
从上面的例子看,yield 就是延迟求值而已。但是 yield 还有一个特性,就是它是一个 expression,有返回值!看例子:
>>> def func():
... r = yield 1
... yield r
...
>>> g = func()
>>> next(g)
1
>>> next(g) # 通过 next 调用,yield 的返回值为 None
>>> g2 = func()
>>> next(g2) # 首先需要通过 next 调用,运行到 yield 语句处
1
>>> g2.send(419) # 现在用 send 方法,这会将当前所在的 yield 语句的值设置为你 send 的值,也就是 419
419 # 然后 generator 运行到下一个 yield,返回右边的值并暂停
generator 有四个实例函数:next、send 是刚刚已经介绍了的,此外还有 throw 用于从 yield 所在处抛出 Exception,和 close 用于关闭 Generator。详见 Generator-iterator methods
2. yield from
可以理解成是 yield <value> from <iterable>
,每次调用时它都会从 <iterable> 中取值,直到遇到 StopIteration。才会从下一个 yield 取值。
>>> def f():
... yield from [1, 2, 3, 4] # iterable
... yield 5
... yield from range(4, 0, -1) # iterable
...
>>> list(f())
[1, 2, 3, 4, 5, 4, 3, 2, 1]
当然,yield from <iterable>
也是一个 expression,也有值。它的值就是 StopIteration 异常的第一个参数,内置类型的这个值都是 None.
>>> def f():
... r = yield from [1, 2]
... yield f"value of yield from is {r}"
...
>>> list(f())
[1, 2, 'value of yield from is None']
当 <iterable> 是 generator 时,yield from
会直接将函数调用委托给这个子 generator,这里的调用包括了前面说过的 next、send、throw、close 四个函数。
并直接将 sub generator yield 的值 yield 给 caller.
3. yield 和 return 混用会发生什么?
generator 中的 return value
,语义上等同于 rasie StopIteration(value)
:
>>> def f():
... yield 1
... return 2
... yield 3 # 永远不会被执行
...
>>> g = f()
>>> next(g)
1
>>> next(g) # return 引发 StopIteration
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration: 2
>>> next(g) # 再次调用,StopIteration 变成无参了。
Traceback (most recent call last):
File "<input>", line 1, in <module>
StopIteration
可以看到 return 引发了 StopIteration 异常,而 return 的值则成了该异常的第一个参数。
之前说过 yield from <sub generator>
表达式的值,就是该 <sub generator> 的 StopIteration 异常的第一个参数,因此:
>>> def f2():
... a = yield from f()
... yield a # a 是 f() 中 return 的值
...
>>> list(f2())
[1, 2]
PEP 479 -- Change StopIteration handling inside generators 修改了StopIteration 的行为,该 PEP 使人为 raise 的 StopIteration 引发一个 RuntimeError。
该 PEP 在 Python 3.5 版本添加到 future 中,并在 Python 3.7 成为默认行为。
因此除非你确实想要引发异常,否则应该使用 return 来结束一个 generator 并返回值。
二、异步IO、协程与非阻塞 IO
先了解一下 进程线程协程与并发并行 和 各种 IO 模型
三、asyncio 的简单使用
asyncio 引入了两个新关键字:async 和 await,其中 async 能放在三个地方:
- async def:用于定义异步函数和异步生成器
- 不含有 yield 的是 async def 定义的是协程函数(coroutine function),调用该函数返回协程对象(coroutine object),协程对象需要通过 EventLoop 运行。
- 内部含有 yield 的 async def 定义的是异步生成器函数(asynchronous generator function),调用该函数返回异步生成器(async_generator)
- 异步生成器只能用在 Coroutine 中
- async def 中不允许使用 yield from
- async for:表示 for 迭代的是一个异步生成器,该 for 循环的每一次迭代,都是异步的。
- 只能用在 async def 的内部
- async with:表示 with 管理的是一个异步上下文管理器(asynchronous context manager)
- 该 context manager 的 enter 和 exit 两个步骤是异步的
- 只能用在 async def 的内部
注意异步 generator、context manager,它的 protocol 都和同步的不同,不能混为一谈。
具体而言,对同步 protocol xxx 函数,它的异步版本为 axxx,就是加个 a。
而 await,就相当于 yield from,差别在于 await 是异步的。还有我们关心的是 await 表达式的值,而 yield from 中我们更关心它向上层 yield 的值。
在 yield from 中,当前生成器调用另一个生成器,当前生成器会挂起,直到另一个生成器返回。
但是在 await 中,当前 Coroutine 挂起时, eventloop 会寻找其他 task 来跑,这就利用上了 IO 漫长的等待时间。
async for 是每次迭代都会 await 一次,如果迭代对象是 IO 操作,这个 IO 等待时间就会被利用上。
async with 也是同样,如果 context 的 enter 和 exit 是 IO 操作,这个 IO 时间就会被 eventloop 用于运行其他 task.
使用 asyncio 时,我们要用 async def 将所有的 IO 操作都定义成异步操作。然后在调用时,都使用 await/async for/async with 来调用。
Coroutine、Task 和 Future
首先,每个协程对象,都是一个独立的协程单元,协程对象之间可以异步运行。
协程需要放到 EventLoop 内运行,要运行一个协程 a,有三种方法:
- 通过 asyncio.run(coro) 运行一个协程。
- 该方法会新建一个 EventLoop
- 在另一个协程 b 中通过 await 调用 a。当 b 运行时, a 也会被 task 运行。
- 通过 asyncio.create_task(coro),将需要运行的协程包装成 task,然后通过 task 相关的方法来异步运行它们。
- asyncio.gather(*awaitable_objects): 并发执行所有的 task,阻塞到所有 task 结束。返回一个 result 列表。result 的列表顺序和 future 的顺序一致
- asyncio.as_completed(aws, *, loop=None, timeout=None),和 gather 的区别在于,它返回一个异步迭代器,每次迭代都返回最先完成的一个 future.
concurrent.futures 是进程线程的异步执行,而 asyncio 是基于协程的单线程异步执行
参考
- 从0到1,Python异步编程的演进之路
- 怎么掌握 asyncio
- Python Async/Await入门指南
- 谈谈Python协程技术的演进
- Python Doc - Coroutines
- Python Doc - asyncio
Python 异步编程笔记:asyncio的更多相关文章
- python异步编程模块asyncio学习(二)
尽管asyncio应用通常作为单线程运行,不过仍被构建为并发应用.由于I/O以及其他外部事件的延迟和中断,每个协程或任务可能按一种不可预知的顺序执行.为了支持安全的并发执行,asyncio包含了thr ...
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
- 深入理解 Python 异步编程(上)
http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知 ...
- 深入理解Python异步编程(上)
本文代码整理自:深入理解Python异步编程(上) 参考:A Web Crawler With asyncio Coroutines 一.同步阻塞方式 import socket def blocki ...
- 这篇文章讲得精彩-深入理解 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异步编程之asyncio
python异步编程之asyncio 前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...
- python 异步IO( asyncio) 协程
python asyncio 网络模型有很多中,为了实现高并发也有很多方案,多线程,多进程.无论多线程和多进程,IO的调度更多取决于系统,而协程的方式,调度来自用户,用户可以在函数中yield一个状态 ...
随机推荐
- 访问oracle数据库
如果是本地 :sqlplus system/xxx(用户名/密码) 远程到服务器, sqlplus system/xxx(用户名/密码) @IP:port/orcl(orcl为数据库实例)
- 解决问题的思路(如故事版里面有东西,却不见了)(swift里面开发比较多)
解决问题的思路(如故事版里面有东西,却不见了) 正确效果图: 真机效果图: 内容:不见了 解决步骤:(重点讲解方法1) 1.把背景图隐藏了,如果能出现内容,说明背景图把内容遮住了.那怎么办呢,背景图是 ...
- jzoj100029. 【NOIP2017提高A组模拟7.8】陪审团(贪心,排序)
Description 陪审团制度历来是司法研究中的一个热议话题,由于陪审团的成员组成会对案件最终的结果产生巨大的影响,诉讼双方往往围绕陪审团由哪些人组成这一议题激烈争夺. 小 W 提出了一个甲乙双方 ...
- Linux运维工作中需要掌握的知识
说到工具,在行外可以说是技能,在行内我们一般称为工具,就是运维必须要掌握的工具.我就大概列出这几方面,这样入门就基本没问题了.linux系统如果是学习可以选用redhat或centos,特别是cent ...
- 微信小程序数据传递基本
1.从后台服务器获取数据,然后存在JS,通过数据绑定显示在页面 后台获取数据: getUser: function () { var that = this; //function 里面已经不是thi ...
- Python基础——字典和有序字典
字典 说明: 在 Python 中, 字典 是一系列 键 — 值对 .每个键都与一个值相关联,你可以使用键来访问与之相关联的值.与键相关联的值可以是数字.字符串.列表乃至字典.事实上,可将任何 Pyt ...
- 微信小程序 嵌套循环
前言 入门教程之列表渲染多层嵌套循环,目前官方的文档里,主要是一维数组列表渲染的案例,还是比较简单单一,给刚入门的童鞋还是无从入手的感觉. <view wx:for="{{items} ...
- html5 获取和设置data-*属性值的四种方法讲解
1.获取id的对象 2.需要获取的就是data-id 和 dtat-vice-id的值 一:getAttribute()方法 const getId = document.getElementById ...
- crest value &minimum
public class paixu { public static void main(String[] args) { double temp; double num[]={5.1, 7.12, ...
- atoi 和 atof (把数字字符串转化为数字储存)
int atoi(char *s) 如果字符串内容是整数就返回该整数,否则返回0 double atof(char *s) 同上,不过返回浮点型 #include<iostream> #i ...