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 loop
The 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 coroutines
Coroutine 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 taskOf task
A 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 keywords
Python3.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.
One more hammer.
In addition, there is another important point.
yield from
The back can meetiterable
, also can meetThe future object
/ coroutine object;await
You 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.
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.