Python asyncio中Future对象用法及协程与事件循环通信机制问询
嘿,很高兴你深入研究asyncio的细节,我来一步步解答你的问题:
Future是asyncio里异步任务结果的容器,核心作用是帮你跟踪异步操作的完成状态,获取最终结果(或异常)。下面是几个常见的使用场景和方法:
手动创建并控制Future:如果需要手动管理异步操作的完成状态,建议用事件循环的
loop.create_future()创建实例(别直接用asyncio.Future()构造函数,不同事件循环可能有自定义实现)。比如:import asyncio async def main(): loop = asyncio.get_running_loop() fut = loop.create_future() # 模拟异步操作:2秒后设置Future结果 def set_result(): fut.set_result("异步操作完成!") loop.call_later(2, set_result) # 等待Future完成并获取结果 result = await fut print(result) asyncio.run(main())给Future绑定回调:如果想在Future完成时立即执行逻辑,可以用
add_done_callback()方法:def on_future_done(fut): print(f"Future结果:{fut.result()}") fut.add_done_callback(on_future_done)同步任务转异步Future:如果有耗时的同步函数,想用异步方式执行,可以用
loop.run_in_executor(),它会返回一个Future对象:import time def blocking_task(): time.sleep(2) return "同步任务完成" async def main(): loop = asyncio.get_running_loop() fut = loop.run_in_executor(None, blocking_task) result = await fut print(result)注意:除非明确知道需求,否则不要手动调用
set_result()或set_exception()——大部分场景下,asyncio的高层API(比如asyncio.gather()、asyncio.wait())或IO操作会自动处理Future的状态。
你说得没错,协程和事件循环之间确实需要一套约定来实现暂停和恢复,这套约定就是Awaitable协议。
用async def定义协程、使用await关键字时,背后的逻辑是:
- 当协程遇到
await some_awaitable时,会暂停执行,并把some_awaitable交给事件循环。 - 这个
some_awaitable必须实现__await__()方法(比如Future、Task或另一个协程),事件循环通过这个方法知道如何等待操作完成。 - 当等待的操作(比如IO就绪、任务完成)触发时,事件循环会唤醒暂停的协程,让它从暂停处继续执行。
你提到的手动yield fd和感兴趣事件的方式,其实是早期生成器协程(用@asyncio.coroutine装饰的生成器)的做法。在async/await语法出现前,asyncio就是通过生成器的yield实现协程暂停的:生成器yield出"等待对象"(比如表示IO事件的对象),事件循环捕获后注册对应的fd和事件,当事件就绪时,再把结果send回生成器,让它恢复执行。
现在更推荐用async/await语法,因为它更直观,底层协议也被封装好了——你不需要手动处理fd注册和事件监听,asyncio的高层API(比如asyncio.start_server、asyncio.open_connection)已经帮你完成了这些细节。
你手动通过yield fd和事件实现Echo Server,其实是在模拟asyncio底层的工作原理!不过用asyncio原生API可以更简洁地实现同样功能,比如:
import asyncio async def handle_echo(reader, writer): data = await reader.read(100) message = data.decode() addr = writer.get_extra_info('peername') print(f"Received {message!r} from {addr!r}") print(f"Sending {message!r}") writer.write(data) await writer.drain() print("Close the connection") writer.close() await writer.wait_closed() async def main(): server = await asyncio.start_server( handle_echo, '127.0.0.1', 8888) addr = server.sockets[0].getsockname() print(f'Serving on {addr}') async with server: await server.serve_forever() asyncio.run(main())
这个例子里,asyncio.start_server会自动把socket fd注册到事件循环、监听连接事件;await reader.read()会等待socket可读,事件循环在fd就绪时唤醒协程——这些底层操作都被封装起来了,你只需要关注业务逻辑。如果想深入理解底层,可以看看asyncio的SelectorEventLoop实现,它基于select/poll/epoll管理fd,当fd就绪时触发回调恢复协程。
内容的提问来源于stack exchange,提问作者Nishant

