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

Python3.10正常运行的asyncio代码在3.8报错,如何修复?

问题:Python 3.8中asyncio Semaphore引发跨事件循环错误

以下代码在Python 3.10.6(Fedora 35)可正常运行:

import asyncio

sem: asyncio.Semaphore = asyncio.Semaphore(2)


async def async_run() -> None:
    async def async_task() -> None:
        async with sem:
            await asyncio.sleep(1)
            print('spam')

    await asyncio.gather(*[async_task() for _ in range(3)])


asyncio.run(async_run())

但在Python 3.8.10(Ubuntu 20.04)运行时抛出错误:

Traceback (most recent call last):
  File "main.py", line 21, in <module>
    asyncio.run(async_run())
  File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "main.py", line 18, in async_run
    print(future_entry_index, await future_entry)
  File "/usr/lib/python3.8/asyncio/tasks.py", line 619, in _wait_for_one
    return f.result()  # May raise f.exception().
  File "main.py", line 11, in async_task
    async with sem:
  File "/usr/lib/python3.8/asyncio/locks.py", line 97, in __aenter__
    await self.acquire()
  File "/usr/lib/python3.8/asyncio/locks.py", line 496, in acquire
    await fut
RuntimeError: Task <Task pending name='Task-4' coro=<async_run.<locals>.async_task() running at main.py:11> cb=[as_completed.<locals>._on_completion() at /usr/lib/python3.8/asyncio/tasks.py:606]> got Future <Future pending> attached to a different loop

排查后确认是Semaphore对象导致的问题,移除后代码可运行但不符合需求。由于loop参数在Python 3.8已弃用、3.10已移除,无法通过传入loop参数解决。需要修改代码使其在Python 3.8中正常运行。

用户补充:asyncio不同版本差异大,但Semaphore在3.8中应该没问题?

解决方案

问题根源是全局Semaphore在Python 3.8中会绑定到第一个创建的事件循环,但asyncio.run()每次调用都会创建新的事件循环,导致后续任务的循环和Semaphore绑定的循环不一致。

修改方法很简单:把Semaphore的创建放到async_run()内部,确保它和任务在同一个事件循环中初始化:

import asyncio


async def async_run() -> None:
    # 将Semaphore移到内部,和当前任务的事件循环绑定
    sem: asyncio.Semaphore = asyncio.Semaphore(2)
    
    async def async_task() -> None:
        async with sem:
            await asyncio.sleep(1)
            print('spam')

    await asyncio.gather(*[async_task() for _ in range(3)])


asyncio.run(async_run())

为什么这样可行?因为asyncio.run()会在进入async_run()协程后才创建事件循环,此时初始化Semaphore会自动绑定到这个新循环,后续的async_task都在同一个循环中执行,就不会出现"不同循环"的错误了。

Python 3.10修复了这个问题,允许Semaphore在不同循环间复用,但3.8及更早版本需要遵循"锁/信号量和任务同循环创建"的规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 10:31:19