asyncio是从Python 3.4+开始引入的标准库,从而支持async IO,协程(coroutine)。
举个例子:假设有1个洗衣房,里面有10台洗衣机,有一个洗衣工在负责这10台洗衣机。那么洗衣房就相当于1个进程,洗衣工就相当1个线程。如果有10个洗衣工,就相当于10个线程,1个进程是可以开多线程的。这就是多线程!
那么协程呢?先不急。大家都知道,洗衣机洗衣服是需要等待时间的,如果10个洗衣工,1人负责1台洗衣机,这样效率肯定会提高,但是不觉得浪费资源吗?明明1 个人能做的事,却要10个人来做。只是把衣服放进去,打开开关,就没事做了,等衣服洗好再拿出来就可以了。就算很多人来洗衣服,1个人也足以应付了,开好第一台洗衣机,在等待的时候去开第二台洗衣机,再开第三台,……直到有衣服洗好了,就回来把衣服取出来,接着再取另一台的(哪台洗好先就取哪台,所以协程是无序的)。这就是计算机的协程!洗衣机就是执行的方法。
当你程序中方法需要等待时间的话,就可以用协程,效率高,消耗资源少。
好了!现在来总结一下:
洗衣房 ==> 进程
洗衣工 ==> 线程
洗衣机 ==> 方法(函数)
函数前+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
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
当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的定义了。
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