Through the foreshadowing of the first two sections (about the use of coroutines), today we can finally introduce the focus of our entire series - asyncio
.
asyncio
It is a standard library introduced in Python 3.4, and directly supports asynchronous IO.
Some students may very puzzled, since it had to generator-based coroutine, we directly use yield
and yield from
do not realize you can manually scheduling of the IO yet? Why Python has nothing to do when it's full, and keeps making wheels again and again.
This question is easy to answer, just why there is Django
, why there is Scrapy
, it is a truth.
They are all frameworks. Many tasks with high repetitiveness and high complexity will be done for you in advance, so that you can focus on the development of business code.
After learning the difficulties of coroutine with Xiaoming, have you also discovered that I have mastered the knowledge of coroutine, but I still don’t know how to use it, how to use it, and they all say that it can achieve concurrency, but I still don’t Know how to start?
That's because we still lack a mature framework to help you complete those complex actions. At this time, ayncio
it came into being.
1. How to define/create a coroutine
Remember when we created the generator in the first two chapters, how did we check whether the generator object we created?
We use isinstance()
functions to determine whether it is collections.abc
a Generator
subclass of the class in.
We can also use the same method here.
Just add in front of a function async
key, the function object is a coroutine, by isinstance
function, it is indeed Coroutine
the type.
from collections.abc import Coroutine
async def hello(name):
print('Hello,', name)
if __name__ == '__main__':
# 生成协程对象,并不会运行函数内的代码
coroutine = hello("World")
# 检查是否是协程 Coroutine 类型
print(isinstance(coroutine, Coroutine)) # True
In the first two sections, we said that generators are the basis of coroutines, so do we have a way to turn a generator into a coroutine directly? The answer is yes.
import asyncio
from collections.abc import Generator, Coroutine
'''
只要在一个生成器函数头部用上 @asyncio.coroutine 装饰器
就能将这个函数对象,【标记】为协程对象。注意这里是【标记】,划重点。
实际上,它的本质还是一个生成器。
标记后,它实际上已经可以当成协程使用。后面会介绍。
'''
@asyncio.coroutine
def hello():
# 异步调用asyncio.sleep(1):
yield from asyncio.sleep(1)
if __name__ == '__main__':
coroutine = hello()
print(isinstance(coroutine, Generator)) # True
print(isinstance(coroutine, Coroutine)) # False
2. Several concepts of asyncio
Before understanding asyncio
the usage method, it is necessary to first introduce these concepts throughout.
event_loop 事件循环
: The program opens an infinite loop, and the programmer will register some functions (coroutines) on the event loop. When a satisfying event occurs, the corresponding coroutine function is called.coroutine 协程
: A coroutine object refers to a function defined using the async keyword. Its call will not execute the function immediately, but will return a coroutine object. The coroutine object needs to be registered in the event loop and called by the event loop.future 对象
: Represents the result of tasks that will be performed or not performed in the future. There is no essential difference between it and tasktask 任务
: A coroutine object is a function that can be suspended natively, and a task further encapsulates the coroutine, which contains various states of the task. The Task object is a subclass of Future, which associates coroutine with Future, and encapsulates coroutine into a Future object.async/await 关键字
: Python3.5 is used to define keywords for coroutines, async defines a coroutine, and await is used to suspend blocking asynchronous call interfaces. Its effect is similar to yield to some extent.
These concepts may be difficult to understand if you look at them. It's okay. If you look at the examples, and then come back, I believe you will be able to understand them.
3. Learn how coroutines work
The complete workflow of the coroutine is like this
- Define/create coroutine objects
- Turn the coroutine into a task task
- Define event loop object container
- Throw the task task into the event loop object to trigger
Just say not to practice fake tricks, let's take a look
import asyncio
async def hello(name):
print('Hello,', name)
# 定义协程对象
coroutine = hello("World")
# 定义事件循环对象容器
loop = asyncio.get_event_loop()
# task = asyncio.ensure_future(coroutine)
# 将协程转为task任务
task = loop.create_task(coroutine)
# 将task任务扔进事件循环对象中并触发
loop.run_until_complete(task)
The output is of course obvious
Hello, World
4. Comparison of await and yield
Earlier we said that await
the asynchronous call interface used to suspend blocking. Its effect is 一定程度上
similar to yield.
Note that this is, to a certain extent, it means the same effect (both can achieve the pause effect), but the function is not compatible. That is, you can't use it in a generator await
, nor can it be used in a coroutine defined by async yield from
.
Xiao Ming is not talking nonsense. There is a real hammer. One more hammer.
In addition, there is one more important thing.
yield from
It can be connected later可迭代对象
, or it can be connected tofuture对象
/coroutine objects;await
Must be connected laterfuture对象
/协程对象
How to verify it?
yield from
Can be followed later 可迭代对象
, this has been mentioned in the first two chapters, so I won't repeat it here. Next, just verify, yield from
and await
both can be connected future对象
/ 协程对象
that's it.
Before verification, we must first introduce this function: asyncio.sleep(n)
This is a tool function that comes with asyncio. It can simulate IO blocking, and what it returns is a coroutine object.
func = asyncio.sleep(2)
print(isinstance(func, Future)) # False
print(isinstance(func, Coroutine)) # True
Also, learn how to create Future对象
, otherwise how to verify. As mentioned in the previous concept, Task is a subclass of Future, so we only need to create a task object.
import asyncio
from asyncio.futures import Future
async def hello(name):
await asyncio.sleep(2)
print('Hello, ', name)
coroutine = hello("World")
# 将协程转为task对象
task = asyncio.ensure_future(coroutine)
print(isinstance(task, Future)) # True
Okay, next, start verification.
5. Binding callback function
The realization principle of asynchronous IO is to suspend where the IO is high, and then continue execution after the IO ends. In most of the time, the execution of our subsequent code needs to rely on the return value of IO, which requires callbacks.
There are two implementations of callbacks, one is the callback implemented by synchronous programming that most programmers like. This requires us to be able to obtain the return value of the coroutine await.
import asyncio
import time
async def _sleep(x):
time.sleep(2)
return '暂停了{}秒!'.format(x)
coroutine = _sleep(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
loop.run_until_complete(task)
# task.result() 可以取得返回结果
print('返回结果:{}'.format(task.result()))
Output
返回结果:暂停了2秒!
There is also a function of adding a callback function that comes with asyncio.
import time
import asyncio
async def _sleep(x):
time.sleep(2)
return '暂停了{}秒!'.format(x)
def callback(future):
print('这里是回调函数,获取返回结果是:', future.result())
coroutine = _sleep(2)
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coroutine)
# 添加回调函数
task.add_done_callback(callback)
loop.run_until_complete(task)
Output
这里是回调函数,获取返回结果是: 暂停了2秒!
emmm, the result is the same as above. nice