AsyncIO - getting started

Python AsyncIO

asyncio is a standard library introduced from Python 3.4 +, which supports async IO and coroutine.

For example: suppose there is a laundry room with 10 washing machines, and a washer is in charge of these 10 washing machines. Then the laundry is equivalent to a process and the laundryman is equivalent to a thread. If there are 10 laundrymen, it is equivalent to 10 threads. One process can open multiple threads. This is multithreading!

What about synergy? Don't worry. As we all know, washing clothes with a washing machine requires waiting time. If there are 10 washing workers and one person is responsible for one washing machine, the efficiency will certainly be improved, but don't you think it's a waste of resources? It takes 10 people to do what one person can do. Just put the clothes in and turn on the switch. There's nothing to do. Just take them out after they're washed. Even if many people come to wash clothes, one person can cope with it. Open the first washing machine, open the second washing machine while waiting, and then open the third one,... Until the clothes are washed, come back and take out the clothes, and then take another one (which one is washed first, so the process is disordered). This is the collaborative process of computer! The washing machine is the method of execution.

When the method in your program needs waiting time, you can use collaborative process, which is efficient and consumes less resources.

okay! To sum up:

Laundry = = > process

Laundryman = = > thread

Washing machine = = > method (function)

1. async await

When + async keyword is used before the function, the wrapper of the function is actually created. When this function is called, a coroutine object will actually be returned

First, use async awit normally

import asyncio

async def main():
    print("Hello World")

print(main())
<coroutine object main at 0x7feada389ec0>
RuntimeWarning: coroutine 'main' was never awaited
  print(main())
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

Coroutine object is different from normal function. If you want to wait for the execution result of coroutine object, you need to use keyword await to wait for the result returned by coroutine. Let's try await:

import asyncio

async def main():
    print("Hello World")

await main()
File "/Users/xx/PycharmProjects/pythonProject/test_ansy.py", line 15
    await main()
    ^
SyntaxError: 'await' outside function

Await can only be used in async functions. However, await needs to be used in async function, and to run async function, await needs to wait for the return result. Event loop is required

2. Event-loop

Event loop is a design pattern that waits for and distributes events or messages in a program

Python coroutine needs to run in event loop

Asyncio this package provides an asyncio The function of run can be used as the entry of coroutine, asyncio Run will create an event loop, and then execute the coroutine object passed to it on this event loop, usually asyncio The run function will only be called once in the program as the entry of coroutine

import asyncio
async def main():
    print("Hello World")

asyncio.run(main())
Hello World
import asyncio
async def main():
    print("Hello World")
    await foo("I am foo")
    print("foo done")


async def foo(text):
    print(text)
    # Currently, coroutine gives up running and returns after 5 seconds
    await asyncio.sleep(5)
    print("wake up")

asyncio.run(main())

Hello World
I am foo
wake up
foo done

asyncio.new_event_loop() to create a new event loop, and then through loop run_ until_ Complete () to start a coroutine

import asyncio


async def main():
    print("Hello World")
    await foo("I am foo")
    print("foo done")


async def foo(text):
    print(text)
    await asyncio.sleep(2)
    print("wake up foo")

loop = asyncio.new_event_loop()
loop.run_until_complete(main())
Hello World
I am foo
wake up foo
foo done

3. task

When the foo function sleep, we don't want to wait for it to end. We want to do something else during this period. You can use asyncio create_ task. asyncio. create_ Task will get the currently running event loop, and then schedule a task on this loop. This task will start running as soon as possible. Task is a future like object(Future will be mentioned later). A coroutine runs on this object. The significance of task is to let users run coroutine on event loop. So why as soon as possible? Let's look at the following example first

import asyncio


async def main():
    print("Hello World")
    task = asyncio.create_task(foo("I am foo"))
    print("foo done")
    await task


async def foo(text):
    print(text)
    await asyncio.sleep(1)
    print("wake up")

asyncio.run(main())
Hello World
foo done
I am foo
wake up

Surprise! "Foo done" I am foo "It was printed out before. This is because the create_task only schedule d a task on the running event loop and did not start running immediately. Because the main has not suspended the execution, entered the wait, and the execution has not ended, the main will continue to execute. When the main executes the await task, the main suspends the execution, enters the wait, and the next task on the event loop will start to execute, that is It's foo. So we see "foo done" first, then "I am foo". Then foo enters sleep, pauses execution and waits, while main() is waiting for the execution result of foo. There are no other tasks to continue execution on event loop, so the program waits for the end of asyncio.sleep(1) of foo, and finally prints "wake up".

The above scheduling method is called cooperative scheduling. An event loop runs only one Task at a time. When one Task awaits another Task(Future) is completed, the current Task will temporarily stop execution and wait for the results of Future. Then event loop will let other tasks, Future callback (mentioned later), or IO start execution.

Here we will mention asyncio The definition of future.

Future

asyncio. create_ The return of task is task. The definition of task is:

asyncio.tasks

class Task(Future[_T], Generic[_T])

future is

asyncio.futures

class Future(Awaitable[_T], Iterable[_T])

Task inherits Future, which represents the final result of an async operation in the Future. To wait for the task to complete and return the final result, we can use the await keyword mentioned earlier, because Future is Awaitable.

import asyncio


async def main():
    print("Hello World")
    task = asyncio.create_task(foo("I am foo"))
    await task
    print("foo done")


async def foo(text):
    print(text)
    await asyncio.sleep(1)
    print("wake up")

asyncio.run(main())
Hello World
I am foo
wake up
foo done

It can be seen here that the await task in main() makes main wait for foo execution to finish before continuing to print "foo done".

You may also want to ask, how do we get the return result of the task

import asyncio


async def main():
    task_foo = asyncio.create_task(foo("I am foo"))
    task_bar = asyncio.create_task(bar())

    foo_res = await task_foo
    await task_bar
    print(foo_res)


async def foo(text):
    print(text)
    await asyncio.sleep(4)
    print("wake up")
    return "returns " + text


async def bar():
    for i in range(10):
        print(i)
        await asyncio.sleep(1)


asyncio.run(main())
I am foo
0
1
2
3
wake up
4
5
6
7
8
9
returns I am foo

res = await task allows the return value of the task to be assigned to res.

When the callback is completed, it can also be used to set the Future

import asyncio


async def main():
    task_foo = asyncio.create_task(foo("I am foo"))
    task_foo.add_done_callback(callback)
    foo_res = await task_foo
    print(foo_res)


async def foo(text):
    print(text)
    await asyncio.sleep(1)
    print("wake up")
    return "returns " + text


def callback(future):
    print("Future has result:", future.result())
    print("I am callback")


asyncio.run(main())
I am foo
wake up
Future has result: returns I am foo
I am callback
returns I am foo

Keywords: Python Linux Multithreading coroutine

Added by anindya23 on Sat, 05 Mar 2022 07:29:42 +0200