Python3 学习笔记(异步IO)
由于CPU的速度远远快于磁盘、网络等IO,在一个线程中CPU执行代码的速度极快,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作,这种情况称为同步IO。
异步IO是当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了,一段时间后,当IO返回结果时,再通知CPU进行处理。
同步:就是发出一个“调用”时,在没有得到结果之前,该“调用”就不返回,“调用者”需要一直等待该“调用”结束,才能进行下一步工作。
异步:“调用”在发出之后,就直接返回了,也就没有返回结果。“被调用者”完成任务后,通过状态来通知“调用者”继续回来处理该“调用”。
协程,又称微线程,纤程,英文名Coroutine。在执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。
Python对协程的支持是通过generator
(生成器)实现的。在Python3.4中,协程都是通过使用yield from
和asyncio
模块中的@asyncio.coroutine来实现的。asyncio
专门被用来实现异步IO操作。
生成器中的关键字yield
可以实现中断功能,可以传出值,也可以从函数外部接收值,而yield from
的实现就是简化了yield
操作。
|
|
执行结果如下:
|
|
yield from
还有一个主要功能是省去了很多异常的处理,其内部已经实现大部分异常处理。
在协程中,只要是和IO任务类似的、耗费时间的任务都可以使用yield from
来进行中断,达到异步功能。
|
|
执行结果如下:
|
|
执行过程:
-
首先通过
get_event_loop()
获取了一个标准事件循环loop(因为是一个,所以协程是单线程)。 -
然后通过
run_until_complete(main())
来运行协程(此处把调用方协程main()作为参数,调用方负责调用其他委托生成器),该函数的特点是直到循环事件的所有事件都处理完才能完整结束。 -
进入调用方协程,把多个任务
taskIO_1()
和taskIO_2()
放到一个task
列表中,可理解为打包任务。 -
现在使用
asyncio.wait(tasks)
来获取一个awaitable objects
,即可等待对象的集合(此处的aws是协程的列表),并发运行传入的aws,同时通过yield from
返回一个包含(done, pending)
的元组,done
表示已完成的任务列表,pending
表示未完成的任务列表;如果使用asyncio.as_completed(tasks)
则会按完成顺序生成协程的迭代器(常用于for循环中),因此当你用它迭代时,会尽快得到每个可用的结果。此外,当轮询到某个事件时(如taskIO_1()
),直到遇到该任务中的yield from
中断,开始处理下一个事件(如taskIO_2()
),当yield from
后面的子生成器完成任务时,该事件才再次被唤醒。 -
因为
done
里面有需要的返回结果,但它目前还是个任务列表,所以要取出返回的结果值,要遍历它并逐个调用result()
取出结果即可。(注:对于asyncio.wait()
和asyncio.as_completed()
返回的结果均是先完成的任务结果排在前面,所以此时打印出的结果不一定和原始顺序相同,但使用gather()
的话可以得到原始顺序的结果集。) -
最后通过
loop.close()
关闭事件循环。
在Python 3.5开始引入了新的语法async
和await
,以简化并更好地标识异步IO。
要使用新的语法,只需要做两步简单的替换:
- 把
@asyncio.coroutine
替换为async
; - 把
yield from
替换为await
。
更改上面的代码如下,可得到同样的结果:
|
|
- 原文作者:百年孤独
- 原文链接:https://qoanty.github.io/2019/04/python3-asyncio/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。