PyO3导出Rust异步函数供Python调用时future线程安全错误如何解决
问题根因
future cannot be sent between threads safely报错的核心是违反了pyo3_asyncio的约束:
pyo3_asyncio::tokio::future_into_py要求传入的Future必须实现Sendtrait,因为tokio多线程运行时会将Future调度到不同线程执行- 你将非
Send的Python<'_>实例直接move进了async闭包,Python类型和当前线程的GIL绑定,不能跨线程传递,直接导致Future不满足Send约束 - 额外的错误点:你不应该在跨线程的Future内部调用
into_py转换Python对象,这一步操作需要持有GIL,在异步块内执行会导致线程安全问题
修复方案
直接修改导出函数即可:
#[pyfunction] fn get_weather(py: Python) -> PyResult<&PyAny> { // 不要将py变量move进async闭包 pyo3_asyncio::tokio::future_into_py(py, async move { // 异步块内只执行纯Rust逻辑,拿到原生Weather结构体 let weather = get_weather().await.map_err(|e| { // 将Rust侧错误转换为Python可识别的异常,不要直接用unwrap pyo3::exceptions::PyRuntimeError::new_err(format!("获取天气失败: {}", e)) })?; // 直接返回Rust原生结构体即可,PyO3会自动在GIL上下文中转换为Python对象 Ok(weather) }) }
额外注意事项
- 如果你用到的HTML解析逻辑有CPU密集型操作,建议将解析部分放到
tokio::task::spawn_blocking中执行,避免阻塞异步运行时 - 请确保Cargo.toml中依赖版本匹配,推荐用pyo3和pyo3-asyncio的0.20及以上稳定版本:
[dependencies] pyo3 = { version = "0.20", features = ["extension-module"] } pyo3-asyncio = { version = "0.20", features = ["tokio-runtime"] } reqwest = { version = "0.11", features = ["native-tls", "tokio"] } scraper = "0.18" tokio = { version = "1.32", features = ["rt-multi-thread", "time"] }
内容的提问来源于stack exchange,提问作者alfex4936
相关产品推荐
相关产品推荐

