You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何等待协程仍被视为非阻塞?异步机制核心疑问解析

关于Python协程异步的核心问题解答

问题1:既然需要等待,为何仍被视为异步操作?

同步代码中,每发起一个网络请求,整个线程会被完全阻塞——啥也干不了,必须等这个请求的响应返回、解析完成,才能执行下一个请求。总耗时是所有请求耗时的累加。

而异步代码里,await的等待是非阻塞式等待:当你await一个耗时操作(比如网络请求)时,当前协程会暂停执行,但事件循环不会闲置,它会立刻去调度其他已经准备好的协程(比如发起下一个请求)。所有请求是并发发起的,总耗时接近单个请求的最长耗时,而非所有请求耗时之和,这就是异步性能提升的核心原因。

问题2:“将控制权交还给事件循环”具体指什么?这是不是意味着await resp.json()之后的print语句可以提前执行?

“交还控制权给事件循环”的意思是:当前协程因await某个未完成的操作(比如等待网络响应)暂停时,CPU的控制权会交还给事件循环,由它去执行其他就绪的协程(比如处理下一个请求的发送)。

但**await之后的代码绝对不会提前执行**。await的语义就是“必须等这个操作完成后,再继续执行后续代码”。对单个协程来说,pokemon = await resp.json()之后的print(pokemon['name']),必须等JSON解析完成才会运行。只是在等待的这段空窗期里,事件循环可以去处理其他协程的任务,实现了并发效率的提升。

代码对比解析

异步代码(150次GET请求)

import aiohttp
import asyncio
import time

start_time = time.time()


async def main():

    async with aiohttp.ClientSession() as session:

        for number in range(1, 151):
            pokemon_url = f'https://pokeapi.co/api/v2/pokemon/{number}'
            async with session.get(pokemon_url) as resp:
                pokemon = await resp.json()
                print(pokemon['name'])

asyncio.run(main())
print("--- %s seconds ---" % (time.time() - start_time))

循环中每个session.get异步发起请求,遇到await resp.json()时当前协程暂停,事件循环立刻切换到下一个请求的发起。所有请求几乎同时发出,总耗时大幅缩短。

同步代码(150次GET请求)

import requests
import time

start_time = time.time()

for number in range(1, 151):
    url = f'https://pokeapi.co/api/v2/pokemon/{number}'
    resp = requests.get(url)
    pokemon = resp.json()
    print(pokemon['name'])

print("--- %s seconds ---" % (time.time() - start_time))

requests.get是阻塞式调用,发起请求后线程完全停滞,必须等响应返回、解析完成,才能执行下一个请求。150个请求串行执行,总耗时是所有请求耗时的累加,效率远低于异步版本。


内容的提问来源于stack exchange,提问作者user1224478

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 10:57:21