1.asyncio.run()
可以单独运行协程
asyncio.run()
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
print(f"started at {time.strftime('%X')}")
await say_after(1, 'hello')
await say_after(2, 'world')
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
结果
started at 10:25:16
hello
world
finished at 10:25:19
Process finished with exit code 0
2.asyncio.create_task()
asyncio.create_task()
函数用来并发运行作为 asyncio 任务
的多个协程。
修改上面的代码如下:
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
task1 = asyncio.create_task(
say_after(1, 'hello'))
task2 = asyncio.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
# Wait until both tasks are completed (should take
# around 2 seconds.)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())
结果:
started at 10:38:18
hello
world
finished at 10:38:20
Process finished with exit code 0
因为两个say_after()协程并发进行了,所以比之前少了1秒。
3.await
await 可用于可等待对象:协程、任务和future,这三个下面会介绍
(1)协程:用async 关键字定义的协程函数
import asyncio
async def nested():
return 42
async def main():
# Nothing happens if we just call "nested()".
# A coroutine object is created but not awaited,
# so it *won't run at all*.
nested()
# Let's do it differently now and await it:
print(await nested()) # will print "42".
asyncio.run(main())
(2) 任务task
为了并发,将协程打包起来(或者说加入日程)之后的结果就叫任务
当一个协程通过 asyncio.create_task()
等函数被打包为一个 任务,该协程将自动排入日程准备立即运行:
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
(3) futures
这部分还不太理解,后续补充:
一种异步操作的返回结果,是一种低级的可等待对象,一般用于有回调函数的地方,在应用层一般不推荐创建futures对象,
当future对象被等待的时候意味着,这个协程一直会等待直到这个future涉及的对象在其他地方处理完毕。
4. 协程的运行
(1.)asyncio.run(coro, *, debug=False)
这个方法每次调用时会自动创建一个循环,用新建的循环运行传入的协程,结束后自动关闭,
当一个线程中有其他的loop在运行时,这个方法不能被调用。
debug为true时为调试模式下进行。
(2.)asyncio.create_task(coro, *, name=None)
创建任务,让协程并发
name不为空时,会使用 Task.set_name()
设置任务名称
所有加入到任务里的协程将会在 get_running_loop()
返回的循环中进行,如果当前的线程中没有正在运行的loop,则会抛出RuntimeError
异常。
我理解到的这种异常情况有如下:一个线程只会有一个循环事件,而在碰到协程时会创建子循环事件,如果一个线程要被创建两个线程或者当一个协程在没有loop的线程中被执行时就会报这个错。
需要注意有以下:在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future()
函数。
In Python 3.7+
task = asyncio.create_task(coro())
...
This works in all Python versions but is less readable
task = asyncio.ensure_future(coro())
5. 睡眠
coroutine asyncio.sleep(delay, result=None, *, loop=None)
在py3.8以上中不推荐使用loop参数,loop参数将在3.10版本中被删除。
result参数可以添加回调内容,当sleep结束后,将会调用回调内容。
import asyncio
import datetime
async def display_date():
loop = asyncio.get_running_loop()
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
await asyncio.sleep(1)
asyncio.run(display_date())
6. 并发进行
awaitable asyncio.gather(aws, loop=None, return_exceptions=False)
并发运行aws参数中的可等待对象。
如果aws中有协程,将自动创建task,将其加入task。
在所有aws可等待结果都运行完成后,将会返回一个结果列表,顺序和aws中传入时顺序一致
如果 return_exceptions 为 False
(默认),所引发的首个异常会立即传播给等待 gather()
的任务。aws* 序列中的其他可等待对象 不会被取消 并将继续运行。
如果 return_exceptions 为 True
,异常会和成功的结果一样处理,并聚合至结果列表。
如果 gather()
被取消,所有被提交 (尚未完成) 的可等待对象也会 被取消。
如果 aws 序列中的任一 Task 或 Future 对象 被取消,它将被当作引发了 CancelledError
一样处理 -- 在此情况下 gather()
调用 不会 被取消。这是为了防止一个已提交的 Task/Future 被取消导致其他 Tasks/Future 也被取消。
在此函数中aws不能是list。
同5中内容,loop参数将被移除。
注解
如果 return_exceptions 为 False,则在 gather() 被标记为已完成后取消它将不会取消任何已提交的可等待对象。 例如,在将一个异常传播给调用者之后,gather 可被标记为已完成,因此,在从 gather 捕获一个(由可等待对象所引发的)异常之后调用 gather.cancel() 将不会取消任何其他可等待对象。
在 3.7 版更改: 如果 gather 本身被取消,则无论 return_exceptions 取值为何,消息都会被传播。
补充:同样功能的还有asyncio.wait函数,后面就将进行补充。