异步编程
这是在阅读petals源码的异步编程时,与必应的对话.
async, await和async with
async, await 和 async with 是Python中用于编写异步代码的关键字¹²³。它们的含义如下:
- async 用于定义一个协程函数,即一个可以在执行过程中暂停和恢复的函数²³。协程函数可以使用 await 表达式来等待另一个协程或异步操作的结果,而不会阻塞当前线程²³。例如:
import asyncio
async def say_hello():
await asyncio.sleep(1) # 等待一秒钟
print("Hello!")
asyncio.run(say_hello()) # 运行协程函数
- await 用于在一个协程函数中暂停当前协程的执行,直到等待的对象(通常是另一个协程或异步操作)返回结果或抛出异常²³。在等待的过程中,当前线程可以切换到其他可运行的协程,从而实现并发²³。例如:
import asyncio
async def add(x, y):
await asyncio.sleep(1) # 等待一秒钟
return x + y
async def main():
result = await add(1, 2) # 等待add协程的结果
print(result)
asyncio.run(main()) # 运行主协程函数
- async with 用于在一个协程函数中使用异步上下文管理器,即一个可以在进入和退出代码块时执行异步操作的对象¹²。它类似于普通的 with 语句,但是可以处理异步资源和异常¹²。例如:
import aiohttp
import asyncio
async def fetch(url):
async with aiohttp.ClientSession() as session: # 创建一个异步会话对象
async with session.get(url) as response: # 发送一个异步GET请求
return await response.text() # 等待响应文本
async def main():
html = await fetch("https://www.python.org") # 等待fetch协程的结果
print(html)
asyncio.run(main()) # 运行主协程函数
源: 与必应的对话, 2023/3/19(1) python - Why do we need
async forandasync with? - Stack Overflow. https://stackoverflow.com/questions/67092070/why-do-we-need-async-for-and-async-with 访问时间 2023/3/19.
(2) Getting Started With Async Features in Python – Real Python. https://realpython.com/python-async-features/ 访问时间 2023/3/19.
(3) Async IO in Python: A Complete Walkthrough – Real Python. https://realpython.com/async-io-python/ 访问时间 2023/3/19.
Why use asyncio.run() instead of call the asychronous function directly?
这是一个很好的问题。要运行一个协程函数,我们需要有一个事件循环来管理所有的异步任务。事件循环是一个无限循环,它不断地检查和执行可运行的任务或协程³。如果我们直接调用协程函数,它只会返回一个协程对象,而不会执行任何代码³。
asyncio.run 函数是一种方便的方法,它可以为我们创建一个事件循环,运行我们传入的协程函数,并在协程函数完成后关闭事件循环¹²³。这样我们就不需要自己手动创建和管理事件循环了⁴。
你可以把 asyncio.run 函数看作是异步程序的入口点,它负责启动和停止事件循环,并运行我们定义的主协程函数²³。
源: 与必应的对话, 2023/3/19(1) Coroutines and Tasks — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-task.html 访问时间 2023/3/19.
(2) Python asyncio.run() function. https://www.asyncio.run/ 访问时间 2023/3/19.
(3) asyncio — Asynchronous I/O — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio.html 访问时间 2023/3/19.
(4) Async IO in Python: A Complete Walkthrough – Real Python. https://realpython.com/async-io-python/ 访问时间 2023/3/19.
(5) python - asyncio run or run_until_complete - Stack Overflow. https://stackoverflow.com/questions/55590343/asyncio-run-or-run-until-complete 访问时间 2023/3/19.
asyncio.shield() and asyncio.wait()
asyncio.shield() 函数可以保护一个任务或协程不被取消⁴⁵。它接受一个可等待的对象作为参数,并返回一个 asyncio.Future 对象。这个Future对象可以直接被等待或者传递给另一个任务或协程⁴⁵。
例如:
import asyncio
async def cancel_me():
print("cancel_me(): before sleep")
try:
# Wait for 1 hour
await asyncio.sleep(3600)
except asyncio.CancelledError:
print("cancel_me(): cancel sleep")
raise
finally:
print("cancel_me(): after sleep")
async def main():
# Create a "cancel_me" Task
task = asyncio.create_task(cancel_me())
# Wait for 1 second
await asyncio.sleep(1)
# Shield task from cancellation
shielded_task = asyncio.shield(task)
# Cancel task
task.cancel()
# Wait for the shielded task to complete
await shielded_task
asyncio.run(main())
这个例子中,我们创建了一个名为 cancel_me 的任务,它会等待一小时。然后我们等待一秒钟,然后用 asyncio.shield() 函数来保护这个任务不被取消。最后我们取消这个任务,并等待被保护的任务完成。
这个例子的输出是:
cancel_me(): before sleep
cancel_me(): after sleep
可以看到,即使我们取消了原始的任务,但是被保护的任务仍然继续执行,并没有抛出 CancelledError 异常⁴⁵。
而 asyncio.wait() 函数则可以运行一个可迭代的可等待对象,并阻塞直到满足指定的条件³。它有以下参数:
- aws 是一个可迭代的可等待对象,你想要并发地运行。
- timeout 是一个可选的超时时间,如果超过了这个时间还没有满足条件,则返回。
- return_when 是一个常量,表示返回的条件。它有以下几种取值:
- ALL_COMPLETED:当所有的可等待对象都完成时返回。
- FIRST_COMPLETED:当第一个可等待对象完成时返回。
- FIRST_EXCEPTION:当第一个可等待对象抛出异常时返回。
- ALL_EXCEPTIONS:当所有的可等待对象都抛出异常时返回。
例如:
import asyncio
# A coroutine function that prints its input and waits for a random time between 0 and 3 seconds.
async def worker(num):
print(f"Worker {num} started")
await asyncio.sleep(random.randint(0,3))
print(f"Worker {num} finished")
# A coroutine function that creates three workers and waits for them using asyncio.wait().
async def main():
tasks = []
for i in range(3):
tasks.append(asyncio.create_task(worker(i)))
done, pending = await asyncio.wait(tasks, timeout=2, return_when=FIRST_COMPLETED)
print(f"{len(done)} tasks are done.")
print(f"{len(pending)} tasks are pending.")
for task in pending:
task.cancel()
# Run the main coroutine function.
asyncio.run(main())
这个例子中,我们创建了三个名为 worker 的协程函数,它们会打印自己的输入并随机地等待一段时间。然后我们用 asyncio.wait() 函数来等待它们,并设置超时时间为2秒,返回条件为第一个完成。最后我们打印出完成和未完成的任务数量,并取消所有未完成
源: 与必应的对话, 2023/3/19(1) Asyncio Shield From Cancellation. https://superfastpython.com/asyncio-shield/ 访问时间 2023/3/19.
(2) Asyncio Shield From Cancellation. https://superfastpython.com/asyncio-shield/ 访问时间 2023/3/19.
(3) Python asyncio.wait(): Running Tasks Concurrently. https://www.pythontutorial.net/python-concurrency/python-asyncio-wait/ 访问时间 2023/3/19.
(4) Coroutines and Tasks — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-task.html 访问时间 2023/3/19.
(5) asyncio — Asynchronous I/O — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio.html 访问时间 2023/3/19.
(6) 深入asyncio.shield - 知乎. https://zhuanlan.zhihu.com/p/75193842 访问时间 2023/3/19.
(7) Coroutines and Tasks — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-task.html 访问时间 2023/3/19.
(8) asyncio — Asynchronous I/O — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio.html 访问时间 2023/3/19.
(9) Async IO in Python: A Complete Walkthrough – Real Python. https://realpython.com/async-io-python/ 访问时间 2023/3/19.
What is asyncio.get_event_loop()
asyncio.get_event_loop() 这个函数的功能是获取当前的事件循环¹。事件循环是asyncio的核心组件,它负责管理和执行协程、任务、回调和IO操作¹。
当这个函数在一个协程或一个回调中被调用时(例如,使用 call_soon 或类似的API来安排),这个函数会总是返回正在运行的事件循环¹。如果没有设置正在运行的事件循环,这个函数会返回 get_event_loop_policy().get_event_loop() 的结果¹。
例如:
import asyncio
# A coroutine function that prints "Hello World" and waits for 1 second.
async def hello():
print("Hello World")
await asyncio.sleep(1)
# Get the current event loop.
loop = asyncio.get_event_loop()
# Run the coroutine until it is complete.
loop.run_until_complete(hello())
# Close the event loop.
loop.close()
这个例子中,我们定义了一个名为 hello 的协程函数,它会打印 "Hello World" 并等待一秒钟。然后我们用 asyncio.get_event_loop() 函数来获取当前的事件循环,并用 run_until_complete() 方法来运行这个协程直到它完成。最后我们用 close() 方法来关闭事件循环。
这个例子的输出是:
Hello World
源: 与必应的对话, 2023/3/19(1) Event Loop — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-eventloop.html 访问时间 2023/3/19.
(2) Asyncio之事件循环EventLoop的使用 - 知乎. https://zhuanlan.zhihu.com/p/69210021 访问时间 2023/3/19.
(3) 关于使用python协程模块asyncio创建协程添加进get_event_loop后协程。。。_百度文库. https://wenku.baidu.com/view/397854577cd5360cba1aa8114431b90d6d858951.html 访问时间 2023/3/19.
(4) When to use asyncio.get_running_loop() vs asyncio.get_event_loop()?. https://stackoverflow.com/questions/65206110/when-to-use-asyncio-get-running-loop-vs-asyncio-get-event-loop 访问时间 2023/3/19.
(5) Python Examples of asyncio.get_event_loop - ProgramCreek.com. https://www.programcreek.com/python/example/81225/asyncio.get_event_loop 访问时间 2023/3/19.
What is event loop?
事件循环是每个asyncio应用程序的核心¹。事件循环运行异步任务和回调,执行网络IO操作,和运行子进程¹。应用程序开发者通常应该使用高级的asyncio函数,例如 asyncio.run(),并且很少需要引用循环对象或调用它的方法¹。
事件循环的工作原理是这样的:
- 事件循环会维护一个任务队列,其中存放了需要执行的协程或回调⁵。
- 事件循环会不断地从任务队列中取出一个任务,并执行它,直到它完成或被暂停⁵。
- 如果一个任务被暂停(例如,因为遇到了 await 表达式),事件循环会将它放回任务队列,并继续执行下一个任务⁵。
- 如果一个任务完成了(例如,因为遇到了 return 语句),事件循环会将它从任务队列中移除,并执行下一个任务⁵。
- 如果所有的任务都完成了或被暂停了,事件循环会等待新的任务加入到队列中⁵。
这样,事件循环可以实现在单线程中并发地运行多个任务。
源: 与必应的对话, 2023/3/19(1) Event Loop — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-eventloop.html 访问时间 2023/3/19.
(2) Python Event Loop. https://www.pythontutorial.net/python-concurrency/python-event-loop/ 访问时间 2023/3/19.
(3) Python Event Loop | Complete Guide to Python Event Loop | Examples - EDUCBA. https://www.educba.com/python-event-loop/ 访问时间 2023/3/19.
(4) Event Loop — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-eventloop.html 访问时间 2023/3/19.
(5) Event Loop — Python 3.7.16 documentation. https://docs.python.org/3.7/library/asyncio-eventloop.html 访问时间 2023/3/19.
What is asyncio.get_event_policy()?
get_event_loop_policy() 这个函数的功能是获取当前的事件循环策略²。事件循环策略是一个全局对象,用于获取和设置当前的事件循环,以及创建新的事件循环²。默认的策略可以被替换为内置的替代方案,以使用不同的事件循环实现,或者被自定义的策略替换,以覆盖这些行为²。
例如:
import asyncio
# Get the current event loop policy.
policy = asyncio.get_event_loop_policy()
# Print the type of the policy object.
print(type(policy))
# Print the type of the default event loop for this policy.
print(type(policy.get_default_loop()))
这个例子中,我们用 asyncio.get_event_loop_policy() 函数来获取当前的事件循环策略,并打印它的类型和它所使用的默认事件循环的类型。
这个例子的输出是:
<class 'asyncio.unix_events._UnixDefaultEventLoopPolicy'>
<class 'asyncio.unix_events._UnixSelectorEventLoop'>
源: 与必应的对话, 2023/3/19(1) Policies — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-policy.html 访问时间 2023/3/19.
(2) Event Loop — Python 3.11.2 documentation. https://docs.python.org/3/library/asyncio-eventloop.html 访问时间 2023/3/19.
(3) 18.5.2. Event loops — Python 3.6.15 documentation. https://docs.python.org/3.6/library/asyncio-eventloops.html 访问时间 2023/3/19.

浙公网安备 33010602011771号