快速掌握AsyncI-新手入门

2022年8月26日13:16:16

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 asyncioasyncdefmain():print("Hello World")print(main())
<coroutineobject main at0x7feada389ec0>
RuntimeWarning: coroutine'main' was never awaitedprint(main())
RuntimeWarning: Enable tracemalloc to get theobject allocation traceback

coroutine object与正常function不同,如果要等待coroutine object的执行结果,需要使用keywordawait来等待coroutine完成返回的结果。我们来试试await:

import asyncioasyncdefmain():print("Hello World")await main()
File"/Users/xxx/PycharmProjects/pythonProject/test_ansy.py", line15await 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 asyncioasyncdefmain():print("Hello World")

asyncio.run(main())
Hello World
import asyncioasyncdefmain():print("Hello World")await foo("I am foo")print("foo done")asyncdeffoo(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 asyncioasyncdefmain():print("Hello World")await foo("I am foo")print("foo done")asyncdeffoo(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 asyncioasyncdefmain():print("Hello World")
    task= asyncio.create_task(foo("I am foo"))print("foo done")await taskasyncdeffoo(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.tasksclassTask(Future[_T], Generic[_T])

future是

asyncio.futuresclassFuture(Awaitable[_T], Iterable[_T])

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

import asyncioasyncdefmain():print("Hello World")
    task= asyncio.create_task(foo("I am foo"))await taskprint("foo done")asyncdeffoo(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 asyncioasyncdefmain():
    task_foo= asyncio.create_task(foo("I am foo"))
    task_bar= asyncio.create_task(bar())

    foo_res=await task_fooawait task_barprint(foo_res)asyncdeffoo(text):print(text)await asyncio.sleep(4)print("wake up")return"returns "+ textasyncdefbar():for iinrange(10):print(i)await asyncio.sleep(1)


asyncio.run(main())
I am foo0123
wake up456789
returns I am foo

res = await task 可以让task的返回值被赋予到res上。

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

import asyncioasyncdefmain():
    task_foo= asyncio.create_task(foo("I am foo"))
    task_foo.add_done_callback(callback)
    foo_res=await task_fooprint(foo_res)asyncdeffoo(text):print(text)await asyncio.sleep(1)print("wake up")return"returns "+ textdefcallback(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
  • 作者:阿杜
  • 原文链接:https://blog.csdn.net/weixin_44621617/article/details/123285715
    更新时间:2022年8月26日13:16:16 ,共 4462 字。