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

Python事件循环异常行为疑问及GIL相关困惑

关于Python异步代码与GIL的疑问

问题代码

import asyncio

async def get_some_values_from_io():
    print("Getsome value Executing...")
    await asyncio.sleep(3)
    return [100,200]

vals = []

async def fetcher():
    while True:
        print("Fetcher Executing...")
        io_vals = await get_some_values_from_io()

        for val in io_vals:
            vals.append(io_vals)

async def monitor():
    while True:
        print("Monitor Executing...")
        print (len(vals))

        await asyncio.sleep(3)

async def main():
    t1 = asyncio.create_task(fetcher())
    t2 = asyncio.create_task(monitor())
    await asyncio.gather(t1, t2)

asyncio.run(main())

print("Rest of the method is executing....")

运行现象

两个异步函数均调用asyncio.sleep(3),预期两者休眠时最后一条print("Rest of the method is executing....")会执行,但实际程序持续循环输出:

Fetcher Executing...
Getsome value Executing...
Monitor Executing...
0
Fetcher Executing...
Getsome value Executing...
Monitor Executing...
2
...

疑问解答

1. 为什么最后一条print语句不会执行?

asyncio.run(main())会阻塞当前线程直到传入的协程(即main)执行完毕。但main里通过asyncio.gather等待的t1和t2都是while True的无限循环协程,永远不会结束,所以asyncio.run永远不会返回,自然不会执行后面的print语句。

asyncio.sleep只是让当前协程暂停,把控制权交还给事件循环,让其他协程有机会运行,但事件循环本身仍被asyncio.run掌控,主线程被事件循环完全占据,不会向下执行后续代码。

2. 关于GIL与事件循环的理解

你的核心理解是正确的:默认情况下,Python异步事件循环运行在单线程中,并且受GIL限制——同一时间只有一个线程能执行Python字节码。

3. run_in_executor与GIL的关系

run_in_executor确实会把任务放到事件循环外的线程(或进程)中运行,这和GIL的特性并不矛盾:

  • GIL的限制是同一时间只有一个线程能执行Python代码,但当线程执行IO密集型任务(比如调用C扩展的IO操作时会主动释放GIL)或非Python代码时,GIL会被释放,其他线程可以获取GIL执行Python代码。
  • run_in_executor默认使用ThreadPoolExecutor,线程池中的线程执行CPU密集型任务时,每个线程仍受GIL限制,但因为这些线程在事件循环之外,它们的执行不会阻塞事件循环运行——事件循环可以继续处理其他异步协程,线程池中的线程则在各自时间片内获取GIL执行任务。
  • 如果是CPU密集型任务,更推荐通过run_in_executor指定ProcessPoolExecutor,因为进程拥有独立的GIL,多个进程可以真正并行执行Python代码,避开GIL的限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:02:40