【问题标题】:Will run_in_executor ever block?run_in_executor 会阻塞吗?
【发布时间】:2020-10-26 21:19:16
【问题描述】:

假设我有这样的网络服务器:

from fastapi import FastAPI
import uvicorn
import asyncio

app = FastAPI()

def blocking_function():
    import time
    time.sleep(5)
    return 42

@app.get("/")
async def root():
    loop = asyncio.get_running_loop()

    result = await loop.run_in_executor(None, blocking_function)
    return result

@app.get("/ok")
async def ok():
    return {"ok": 1}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", workers=1)

据我了解,代码将在默认的 ThreadExecutorPool 中生成另一个线程,然后在线程池中执行阻塞函数。另一方面,考虑 GIL 的工作原理,CPython 解释器将只执行一个线程 100 ticks,然后它会切换到另一个线程以公平并给其他线程一个前进的机会。在这种情况下,如果 Python 解释器决定切换到正在执行 blocking_function 的线程怎么办?它会阻止who解释器等待time.sleep(5)上剩余的任何内容吗?

我问这个问题的原因是我观察到有时我的应用程序会阻塞blocking_function,但是我不完全确定这里发生了什么,因为我的blocking_function 非常特别——它与 COM 对话API 对象通过 win32com 库。我试图排除这是我陷入的一些 GIL 陷阱。

【问题讨论】:

    标签: python multithreading asynchronous python-asyncio fastapi


    【解决方案1】:

    time.sleep(如this question 中所述)和win32com 库(根据this mailing list post)在调用它们时都会释放 GIL,因此它们不会阻止其他线程在阻塞时继续进行.

    回答“高级”问题 - “run_in_executor 是否可以(直接或间接)阻止事件循环?” - 如果您使用ThreadPoolExecutor,答案只会是“是”,并且您在run_in_executor 中执行的代码会阻止未释放 GIL 的工作。虽然这不会完全阻塞事件循环,但这意味着您的事件循环线程和执行器线程都不能并行运行,因为两者都需要获取 GIL 才能取得进展。

    【讨论】:

    • 太好了,这说明了很多。谢谢!
    猜你喜欢
    • 2021-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多