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

为什么使用threading或multiprocessing锁时aiohttp请求会卡住?

问题原因

核心是同步阻塞锁和asyncio异步事件循环的调度逻辑冲突:

  • 你使用的threading.Lock、multiprocessing.Manager.Lock都属于同步阻塞锁,调用acquire()方法时会直接阻塞当前整个线程,直到拿到锁为止,不会主动让出CPU执行权。
  • 协程1先拿到锁,执行到async with s.get('http://icanhazip.com')时,因为是异步IO操作,会主动让出事件循环,让其他就绪的协程执行。
  • 协程2获得执行权后调用lock.acquire(),此时锁被协程1持有,这个同步调用直接把整个线程卡住,事件循环也随之停止调度。
  • 协程1的异步请求需要事件循环调度才能完成后续逻辑、释放锁,但事件循环已经被协程2的阻塞调用卡住,无法调度协程1,最终形成死锁,程序彻底卡住。

为什么换成requests可以正常运行?

requests是同步请求库,执行请求时不会让出线程控制权,会一直占用线程直到请求完成、释放锁,之后协程2才能获得执行权调用lock.acquire(),不存在事件循环被阻塞的情况,因此可以正常运行。

修复方案

如果是单进程单线程的asyncio场景,直接替换为asyncio自带的异步锁asyncio.Lock即可,它的acquire()是异步方法,等待锁时会主动让出事件循环,不会阻塞整个线程。
如果确实需要跨线程/跨进程使用同步锁,需要把阻塞的acquire调用放到线程池中执行,避免阻塞事件循环,示例如下:

async def test(s: ClientSession, lock: Lock, identifier):
    print(f'before acquiring {identifier}')
    # 把同步阻塞的acquire扔到线程池执行,让出事件循环
    await asyncio.to_thread(lock.acquire)
    print(f'before request {identifier}')
    async with s.get('http://icanhazip.com') as r:
        print(f'after request {identifier}')
    lock.release()
    print(f'after releasing {identifier}')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 11:15:03