如何测量异步生成器阻塞主线程的耗时?
测量异步生成器阻塞主线程的时间
要区分异步生成器中同步阻塞代码(如time.sleep)和异步等待(如await asyncio.sleep)的耗时,只统计主线程被阻塞的时间,可以通过并行测试任务的方式实现:
实现代码
import asyncio import time async def measure_blocking_time(async_gen): gen = async_gen() while True: try: wall_start = time.perf_counter() # 并行运行生成器迭代和一个短测试任务 test_task = asyncio.create_task(asyncio.sleep(0.05)) result, _ = await asyncio.gather(anext(gen), test_task) wall_end = time.perf_counter() test_duration = wall_end - wall_start # 判断是否阻塞主线程:测试任务耗时远大于预期则说明被阻塞 if test_duration > 0.06: # 0.05+0.01误差范围 blocking_time = wall_end - wall_start else: # 未阻塞时,统计生成器同步代码的CPU运行时间 cpu_start = time.process_time() # 重新执行一次迭代(仅用于统计CPU时间,实际场景可优化) await anext(gen) cpu_end = time.process_time() blocking_time = cpu_end - cpu_start yield result, blocking_time except StopAsyncIteration: break # 示例生成器 async def run(): for i in range(5): await asyncio.sleep(0.2) yield i async def walk(): for i in range(5): time.sleep(0.3) yield i # 测试执行 async def main(): print("=== 测量run()的阻塞时间 ===") async for val, t in measure_blocking_time(run): print(f"迭代值:{val},阻塞时间:{t:.4f}s") print("\n=== 测量walk()的阻塞时间 ===") async for val, t in measure_blocking_time(walk): print(f"迭代值:{val},阻塞时间:{t:.4f}s") if __name__ == "__main__": asyncio.run(main())
代码说明
- 并行测试逻辑:每次迭代生成器时,同时启动一个
asyncio.sleep(0.05)的测试任务。如果生成器的迭代阻塞了主线程(如walk()中的time.sleep(0.3)),测试任务会被延迟到生成器迭代完成后才执行,此时测试任务的总耗时会远大于0.05秒,我们就将生成器迭代的总耗时作为阻塞时间。 - 非阻塞场景处理:如果生成器的迭代未阻塞主线程(如
run()中的await asyncio.sleep(0.2)),测试任务会按时完成,此时我们统计生成器同步代码的CPU运行时间(几乎为0)作为阻塞时间。
预期输出
=== 测量run()的阻塞时间 === 迭代值:0,阻塞时间:0.0000s 迭代值:1,阻塞时间:0.0000s 迭代值:2,阻塞时间:0.0000s 迭代值:3,阻塞时间:0.0001s 迭代值:4,阻塞时间:0.0000s === 测量walk()的阻塞时间 === 迭代值:0,阻塞时间:0.3002s 迭代值:1,阻塞时间:0.3001s 迭代值:2,阻塞时间:0.3003s 迭代值:3,阻塞时间:0.3002s 迭代值:4,阻塞时间:0.3001s
内容的提问来源于stack exchange,提问作者Artur Laskowski
相关产品推荐
相关产品推荐

