First acquainted with asynchronous IO framework: asyncio Part 1

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.

asyncioIt 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 yieldand yield fromdo 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, ayncioit 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.abca Generatorsubclass of the class in.

We can also use the same method here.

Just add in front of a function asynckey, the function object is a coroutine, by isinstancefunction, it is indeed Coroutinethe 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 asynciothe 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 task
  • task 任务: 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 awaitthe 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. Await cannot be used in ordinary functionsOne more hammer.Cannot use yield in async

In addition, there is one more important thing.

  • yield fromIt can be connected later 可迭代对象, or it can be connected to future对象/coroutine objects;
  • awaitMust be connected later future对象/协程对象

How to verify it?

yield fromCan be followed later 可迭代对象, this has been mentioned in the first two chapters, so I won't repeat it here. Next, just verify, yield fromand awaitboth 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. Verified

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

Guess you like

Origin blog.csdn.net/weixin_36338224/article/details/109282563