当前位置: 首页 > 工具软件 > asyncio > 使用案例 >

快速掌握AsyncIO---新手入门

陈扬
2023-12-01

Python AsyncIO

asyncio是从Python 3.4+开始引入的标准库,从而支持async IO,协程(coroutine)。

举个例子:假设有1个洗衣房,里面有10台洗衣机,有一个洗衣工在负责这10台洗衣机。那么洗衣房就相当于1个进程,洗衣工就相当1个线程。如果有10个洗衣工,就相当于10个线程,1个进程是可以开多线程的。这就是多线程!

那么协程呢?先不急。大家都知道,洗衣机洗衣服是需要等待时间的,如果10个洗衣工,1人负责1台洗衣机,这样效率肯定会提高,但是不觉得浪费资源吗?明明1 个人能做的事,却要10个人来做。只是把衣服放进去,打开开关,就没事做了,等衣服洗好再拿出来就可以了。就算很多人来洗衣服,1个人也足以应付了,开好第一台洗衣机,在等待的时候去开第二台洗衣机,再开第三台,……直到有衣服洗好了,就回来把衣服取出来,接着再取另一台的(哪台洗好先就取哪台,所以协程是无序的)。这就是计算机的协程!洗衣机就是执行的方法。

当你程序中方法需要等待时间的话,就可以用协程,效率高,消耗资源少。

好了!现在来总结一下:

洗衣房 ==> 进程

洗衣工 ==> 线程

洗衣机 ==> 方法(函数)

1. async await

函数前+async keyword时,实际上是创建了这个函数的wrapper,当调用这个函数时,实际上会返回一个coroutine object

首先 正常使用async awit

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与正常function不同,如果要等待coroutine object的执行结果,需要使用keyword await来等待coroutine完成返回的结果。我们来试试await:

import asyncio

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

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

await 只能在async函数中使用。但是await要在async函数中使用,而要跑async函数,需要await等待返回结果。需要使用event-loop

2. Event-loop

Event-Loop是一个在程序中等待并分发事件或者消息的设计模式

Python coroutine需要跑在event-loop中

asyncio这个包中提供了一个asyncio.run的函数,可以作为coroutine的入口,asyncio.run会创建一个event-loop,然后将传递给他的coroutine object执行在这个event-loop上,通常asyncio.run这个函数在程序中只会被调用一次,作为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)
    # 当前coroutine放弃运行,等待5秒后返回
    await asyncio.sleep(5)
    print("wake up")

asyncio.run(main())

Hello World
I am foo
wake up
foo done

asyncio.new_event_loop()来创建一个新的Event-Loop然后通过loop.run_until_complete()来启动一个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

当foo函数sleep的时候,我们不希望等待他结束,我们希望在此期间做一些别的事情,可以使用asyncio.create_task.asyncio.create_task 会获取当前正在运行的event-loop,然后再这个loop上schedule一个task。这个task会尽可能快的开始运行。Task是一个Future-like的object(Future后面会提到),这个object上运行着一个coroutine,Task存在的意义在于让用户在Event-Loop上运行coroutine。那么为什么说是尽可能快的呢?先看接下来这个例子

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"之前被print了出来,这是因为create_task只是在Running Event-Loop上schedule了一个task,并没有立刻开始运行,因为main还没有暂停执行进入等待,也没执行结束,所以main会继续执行。当main执行到await task时,main暂停执行进入等待,下一个在Event-Loop上的task才开始执行,也就是foo。所以我们先看到了"foo done”,然后才是"I am foo"。接下来foo进入了sleep,暂停执行进入等待,而main()在等待foo的执行结果,Event-Loop上已经没有其他可以继续执行的task了,所以程序等待foo的asyncio.sleep(1)结束,最后print “wake up”。

以上这种scheduling的方式叫做cooperative scheduling,一个Event-Loop在同一时间只运行一个Task。当一个Task awaits另一个Task(Future)完成时,当前Task会暂时停止执行,等待Future的结果,然后Event-Loop会让别的Task,Future callback(后面有提到),或者IO开始执行。

这里就要提到asyncio.Future的定义了。

Future

asyncio.create_task的返回是task,task的定义是:

asyncio.tasks

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

future是

asyncio.futures

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

Task它继承了Future,Future代表一个async operation在未来的最终结果。要等待这个task完成返回最终结果,我们可以使用前面提到的await keyword,这是因为Future是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

这里可以看到,main()中的await task让main等待foo执行结束后,才继续print “foo done”。

你可能还想问,我们如何获取到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 可以让task的返回值被赋予到res上。

Future同时也可以用来设置callback,当Future执行完成后,callback会被执行

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
 类似资料: