目录
[一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。](#一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。)
二、asyncio.run()内部则使用await关键字来调用。
三、并发协程asyncio.create_task()和asyncio.TaskGroup
一、最简协程:asyncio.run() 函数用来运行最高层级的入口点。
python
import asyncio
# 定义一个协程函数
async def main():
print('Hello ...')
return 'done'
if __name__ == '__main__':
# 运行协程函数
run = asyncio.run(main())
print(run)
二、asyncio.run()内部则使用await关键字来调用。
python
import asyncio
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())
三、并发协程asyncio.create_task()和asyncio.TaskGroup
python
import 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')}")
# 等待两项任务都完成(两秒,而不是三秒)
await task1
await task2
print(f"finished at {time.strftime('%X')}")
if __name__ == '__main__':
asyncio.run(main())
print(f"finished at {time.strftime('%X')}")
python
import asyncio
import time
async def say_after(delay, what):
await asyncio.sleep(delay)
print(what)
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(
say_after(1, 'hello'))
task2 = tg.create_task(
say_after(2, 'world'))
print(f"started at {time.strftime('%X')}")
# 当上下文管理器退出时,等待是隐式的。
print(f"finished at {time.strftime('%X')}")
if __name__ == '__main__':
asyncio.run(main())
print(f"finished at {time.strftime('%X')}")
四、可等待对象await
如果一个对象可以在 await 语句中使用,那么它就是 可等待 对象。许多 asyncio API 都被设计为接受可等待对象。
可等待 对象有三种主要类型: 协程, 任务 和 Future.
python
import asyncio
async def nested():
return 42
# 协程属于 可等待 对象,因此可以在其他协程中被等待:
async def main():
rs = await nested()
print(rs)
asyncio.run(main())
python
import asyncio
async def nested():
return 42
async def main():
# 将协程封装成一个任务,该协程会被自动调度执行:
task = await asyncio.create_task(nested())
print(task)
# 任务 被用来"并行的"调度协程
asyncio.run(main())
python
import asyncio
async def set_after(fut, delay, value):
await asyncio.sleep(delay)
fut.set_result(value)
async def main():
loop = asyncio.get_running_loop()
fut = loop.create_future()
loop.create_task(
set_after(fut, 1, '... world'))
print('hello ...')
print(await fut)
asyncio.run(main())
五、创建任务asyncio.create_task()
事件循环将只保留对任务的弱引用。 未在其他地方被引用的任务可能在任何时候被作为垃圾回收,即使是在它被完成之前。 如果需要可靠的"发射后不用管"后台任务,请将它们放到一个集合中。
python
import asyncio
async def some_coro(param):
await asyncio.sleep(1)
print(param)
async def main():
# 存储引用
background_tasks = set()
for i in range(10):
task = asyncio.create_task(some_coro(param=i))
# 保存一个指向此函数的结果的引用,以避免任务在执行过程中消失。 事件循环将只保留对任务的弱引用。 未在其他地方被引用的任务可能在任何时候被作为垃圾回收,即使是在它被完成之前。 如果需要可靠的"发射后不用管"后台任务,请将它们放到一个多项集中
background_tasks.add(task)
# 为了防止永远保留对已完成任务的引用,
# 每个任务在完成后进行回调,自己从集合中删除自己的引用。
task.add_done_callback(background_tasks.discard)
# 等待所有任务完成
await asyncio.gather(*background_tasks)
if __name__ == '__main__':
asyncio.run(main())
六、任务组TaskGroup
任务组 合并了一套用于等待分组中所有任务完成的方便可靠方式的任务创建 API。
在等待期间,仍可将新任务添加到分组中 (例如,通过将 tg
传入某个协程并在该协程中调用 tg.create_task()
)。 一旦最后的任务完成并退出 async with
代码块,将无法再向分组添加新任务。
python
import asyncio
async def some_coro(param):
await asyncio.sleep(1)
return param
async def another_coro(tg):
await asyncio.sleep(1)
# 在执行过程中向任务组中添加新的任务
tg.create_task(some_coro(2))
return 3
async def main():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(some_coro(1))
task2 = tg.create_task(another_coro(tg))
print(f"任务完成: {task1.result()}, {task2.result()}")
if __name__ == '__main__':
asyncio.run(main())
七、休眠asyncio.sleep()
sleep()
总是会挂起当前任务,以允许其他任务运行。将 delay 设为 0 将提供一个经优化的路径以允许其他任务运行。 这可供长期间运行的函数使用以避免在函数调用的全过程中阻塞事件循环。
python
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
# 休眠1秒
await asyncio.sleep(1)
asyncio.run(display_date())
八、并发运行任务asyncio.gather()
一个创建然后并发地运行任务等待它们完成的新选择是 asyncio.TaskGroup。 TaskGroup 提供了针对调度嵌套子任务的比 gather 更强的安全保证:如果一个任务(或子任务,即由一个任务调度的任务)引发了异常,TaskGroup 将取消剩余的已排期任务)。
python
import asyncio
# 任务
async def factorial(name, number):
f = 1
for i in range(2, number + 1):
print(f"Task {name}: Compute factorial({number}), currently i={i}...")
await asyncio.sleep(1)
f *= i
print(f"Task {name}: factorial({number}) = {f}")
return f
async def main():
# 并行运行三个任务
L = await asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
)
print(L)
asyncio.run(main())
九、屏蔽取消操作asyncio.shield()
python
import asyncio
from asyncio import shield
async def something():
pass
async def main():
# 任务
task = asyncio.create_task(something())
# 防止任务被取消
res = await shield(task)
# 相当于
# res = await something()
asyncio.run(main())
十、超时
asyncio.timeout()
返回一个可被用于限制等待某个操作所耗费时间的 异步上下文管理器。
python
import asyncio
async def something():
await asyncio.sleep(6)
async def main():
# 超时的抛出异常
async with asyncio.timeout(5):
await something()
asyncio.run(main())
python
import asyncio
async def something():
await asyncio.sleep(6)
async def main():
try:
# 我们不知道启动时的超时时间,填None。
async with asyncio.timeout(None) as cm:
# 当我们知道到超时时间,我们调用reschedule方法填入
new_deadline = asyncio.get_running_loop().time() + 4
cm.reschedule(new_deadline)
await something()
except TimeoutError:
pass
if cm.expired():
print("超时了没有响应")
asyncio.run(main())
asyncio.timeout_at(when)
类似于 asyncio.timeout(),不同之处在于 when 是停止等待的绝对时间,或者为 None
。
python
import asyncio
async def something():
await asyncio.sleep(6)
async def main():
loop = asyncio.get_running_loop()
deadline = loop.time() + 3
try:
async with asyncio.timeout_at(deadline):
await something()
except TimeoutError:
print("处理超时了")
print("继续执行")
asyncio.run(main())
asyncio.wait_for(aw , timeout)
等待 aw 可等待对象 完成,指定 timeout 秒数后超时。
如果发生超时,将取消任务并引发 TimeoutError。
python
import asyncio
async def eternity():
await asyncio.sleep(3600)
print('yay!')
async def main():
try:
await asyncio.wait_for(eternity(), timeout=1.0)
except TimeoutError:
print('超时!')
asyncio.run(main())
十一、等待
asyncio.wait(aws , * , timeout=None , return_when=ALL_COMPLETED)
并发地运行 aws 可迭代对象中的 Future 和 Task 实例并进入阻塞状态直到满足 return_when 所指定的条件。aws 可迭代对象必须不为空。返回两个 Task/Future 集合: (done, pending)
。
与 wait_for() 不同,wait()
在超时发生时不会取消可等待对象。
python
import asyncio
async def eternity():
await asyncio.sleep(1)
print('yay!')
async def main():
# 将协程对象转换为任务对象
task = asyncio.create_task(eternity())
done, pending = await asyncio.wait([task], timeout=2) # 设置超时时间为 2 秒
if pending:
print("Some tasks are still pending.")
asyncio.run(main())
asyncio.as_completed(aws , * , timeout=None)
并发运行aws-iterable中的可唤醒对象。返回协程的迭代器。可以等待返回的每个协程,以从剩余的可迭代对象中获得最早的下一个结果。
python
import asyncio
async def eternity():
await asyncio.sleep(1)
print('yay!')
async def main():
for coro in asyncio.as_completed([eternity()]):
earliest_result = await coro
asyncio.run(main())
十二、在线程中运行asyncio.to_thread()
在不同的线程中异步地运行函数func 。允许在不同的线程中访问来自事件循环的上下文变量。返回一个可被等待以获取 func 的最终结果的协程。
python
import asyncio
import time
async def eternity():
await asyncio.sleep(1)
return "异步IO完成"
def blocking_io():
time.sleep(1)
return "普通IO完成"
async def main():
result = await asyncio.gather(
# 将普通函数转换为异步任务
asyncio.to_thread(blocking_io),
asyncio.sleep(1),
eternity())
print(result)
asyncio.run(main())
十三、跨线程调度
向指定事件循环提交一个协程。(线程安全)
python
import asyncio
async def main():
# 定义协程
coro = asyncio.sleep(1, result=3)
# 获取当前运行的事件循环
loop = asyncio.get_running_loop()
# 使用 run_coroutine_threadsafe 在当前事件循环中运行协程
future = asyncio.run_coroutine_threadsafe(coro, loop)
# 使用 asyncio.wrap_future 将 concurrent.futures.Future 转换为 asyncio.Future
wrapped_future = asyncio.wrap_future(future)
# 等待结果
result = await wrapped_future
print(result)
# 在主线程中运行 main 协程
asyncio.run(main())
十四、内省
asyncio.current_task(loop=None)
返回当前运行的 Task 实例,如果没有正在运行的任务则返回 None
。如果 loop 为 None
则会使用 get_running_loop() 获取当前事件循环。
asyncio.all_tasks(loop=None)
返回事件循环所运行的未完成的 Task 对象的集合。如果 loop 为 None
则会使用 get_running_loop() 获取当前事件循环。
asyncio.iscoroutine(obj)
如果 obj 是一个协程对象则返回 True
。如果 loop 为 None
则会使用 get_running_loop() 获取当前事件循环。
十五、Task 对象
Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。
python
import asyncio
async def cancel_me():
try:
await asyncio.sleep(3600)
except asyncio.CancelledError:
raise
finally:
print('cancel_me(): after sleep')
async def main():
task = asyncio.create_task(cancel_me())
# 等待一秒
await asyncio.sleep(1)
# 取消 task
task.cancel()
try:
await task
except asyncio.CancelledError:
print("main(): cancel_me is cancelled now")
asyncio.run(main())