Author: MING Personal public account: Python programming time personal wechat: MrBensonwon

Note: This series has been updated on wechat official account. To view the latest articles, please pay attention to the public account for access.

Hello, concurrent programming is part nine.

After laying the groundwork for the first two sections (on the use of coroutines), today we can finally introduce asyncio, the focus of our entire series.

Asyncio is a standard library introduced in Python 3.4 that provides direct built-in support for asynchronous IO.

For those of you who might be wondering, now that we have generator-based coroutines, can’t we just use yield and yield from to manually implement IO scheduling? Why does Python keep building wheels when it’s full?

This is an easy question to answer for the same reason that Django was created and Scrapy was created.

They’re frameworks that take a lot of repetitive, complex work and give it to you ahead of time so you can focus on the business code.

After xiao Ming finished learning the difficulties of coroutine, did you also find that I have mastered the knowledge point of coroutine, but I still don’t know how to use it? It is said that it can realize concurrency, but I still don’t know how to start with it?

That’s because we still don’t have a mature framework to help you with those complex actions. And ayncio was born.

How do I define/create coroutines

Remember in the first two chapters, when we created generators, how we checked to see if we were creating generator objects?

We use the isinstance() function to determine if it is a subclass of the Generator class in collections.abc.

We can do the same thing here.

As long as you precede a function with the async keyword, the function object is a Coroutine, which is indeed of type Coroutine by isinstance.

from collections.abc import Coroutine



async def hello(name):

    print('Hello,', name)



if __name__ == '__main__':

    Generate coroutine objects without running the code inside the function

    coroutine = hello("World")



    Check if it is of type Coroutine

    print(isinstance(coroutine, Coroutine))  # True

Copy the code

In the last two videos, we said that generators are the basis of coroutines, so is there a way to take a generator and use it as a coroutine. The answer is yes.

import asyncio

from collections.abc import Generator, Coroutine



' ' '

Just use the @asyncio.coroutine decorator in the header of a generator function

I can call this function object a coroutine object. Notice here is [mark], underline.

In fact, it is essentially a generator.

Once marked, it is actually ready to be used as a coroutine. That will be explained later.

' ' '






@asyncio.coroutine

def hello(a):

    Asyncio.sleep (1):

    yield from asyncio.sleep(1)





if __name__ == '__main__':

    coroutine = hello()

    print(isinstance(coroutine, Generator))  # True

    print(isinstance(coroutine, Coroutine))  # False

Copy the code

Several concepts of Asyncio

Before we understand how asyncio is used, it is necessary to introduce these concepts throughout.

  • Event_loop Indicates an event loopThe program starts an infinite loop in which the programmer registers functions (coroutines) to the event loop. When a satisfying event occurs, the corresponding coroutine function is called.
  • Coroutine coroutinesCoroutine object, a function defined using the async keyword that does not execute the function immediately but returns a coroutine object. Coroutine objects need to be registered with the event loop to be called by the event loop.
  • The future object: represents the result of tasks that will or will not be performed in the future. It is not fundamentally different from task
  • Of taskA coroutine object is a native function that can be suspended, and a task is a further encapsulation of the coroutine, which contains the various states of the task. A Task object is a subclass of Future. It associates a Coroutine with a Future and encapsulates a Coroutine as a Future object.
  • Async/await keywordsPython3.5 is used to define keywords for coroutines, async is used to define a coroutine, await is used to suspend blocking asynchronous call interfaces. Its function is somewhat similar to yield.

These concepts, dry look may be difficult to understand, ok, look at the examples, and then come back, I’m sure you will understand.

How does the learning coroutine work

The complete workflow of coroutines looks like this

  • Define/create coroutine objects
  • Convert coroutines to task tasks
  • Define the event loop object container
  • Triggered when a task is thrown into an event loop object

All talk and no tricks. Let’s take a look

import asyncio



async def hello(name):

    print('Hello,', name)



Define coroutine objects

coroutine = hello("World")



Define the event loop object container

loop = asyncio.get_event_loop()

# task = asyncio.ensure_future(coroutine)



Convert coroutines to task tasks

task = loop.create_task(coroutine)



Throw the task into an event loop object and fire

loop.run_until_complete(task)

Copy the code

The output, of course, is obvious

Hello, World

Copy the code

Await v. yield

We said earlier that await is used to suspend the blocking asynchronous invocation interface. Its function is somewhat similar to yield.

Note that this is, to some extent, the same in effect, but not functionally compatible. Even if you cannot use await in generators, you cannot use yield in coroutines defined by async.

Xiao Ming is not talking nonsense. There are real hammer.

You cannot use await in normal functions

One more hammer.

Yield cannot be used in async

In addition, there is another important point.

  • yield fromThe back can meetiterable, also can meetThe future object/ coroutine object;
  • awaitYou have to pick it upThe future object/Coroutines object

How do you test that?

Yield from can be followed by an iterable, which was covered in the previous two chapters and won’t be repeated here. Next, just validate that both yield from and await can be used with future/coroutine objects.

Sleep (n) : asyncio.sleep(n) : asyncio: sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n) : sleep(n)

func = asyncio.sleep(2)

print(isinstance(func, Future))      # False

print(isinstance(func, Coroutine))   # True

Copy the code

Also, learn how to create Future objects and otherwise validate them. Task is a subclass of Future, so we 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")



Convert coroutine to task

task = asyncio.ensure_future(coroutine)



print(isinstance(task, Future))   # True

Copy the code

Okay, now, let’s verify.

Verification by

Bind the callback function

The asynchronous I/O implementation principle is to suspend the I/O at the high I/O level and continue the EXECUTION after the I/O is complete. Most of the time, our subsequent code execution depends on the return value of the IO, which requires callbacks.

There are two kinds of callback implementations. One is the synchronous programming callback that most programmers like. This requires that we have a way to get the return value of the await of the coroutine.

import asyncio

import time



async def _sleep(x):

    time.sleep(2)

    return 'Pause {} seconds! '.format(x)





coroutine = _sleep(2)

loop = asyncio.get_event_loop()



task = asyncio.ensure_future(coroutine)

loop.run_until_complete(task)



# task.result() returns the result

print('Return result: {}'.format(task.result()))

Copy the code

The output

Return result: Pause for 2 seconds!

Copy the code

The other way is through asyncio’s built-in add callback function.

import time

import asyncio





async def _sleep(x):

    time.sleep(2)

    return 'Pause {} seconds! '.format(x)



def callback(future):

    print('Here is the callback function that returns:', future.result())



coroutine = _sleep(2)

loop = asyncio.get_event_loop()

task = asyncio.ensure_future(coroutine)



Add a callback function

task.add_done_callback(callback)



loop.run_until_complete(task)

Copy the code

The output

Here is the callback function that returns the result: paused2Seconds!

Copy the code

And that’s the same thing as above.