Rust调用异步方法报future cannot be sent between threads safely错误如何解决
错误原因
- 第一个错误来源于你持有了
std::sync::MutexGuard跨await:标准库的MutexGuard本身没有实现Sendtrait(大部分操作系统要求互斥锁的加锁和解锁必须在同一个线程执行,跨线程传递锁守卫会导致未定义行为)。异步运行时会调度Future在不同线程间转移,持有非Send的变量跨await会导致整个Future不满足Send约束,直接报错。你给Safebrowsing实现Send解决不了问题,因为问题出在锁守卫MutexGuard本身,和Safebrowsing无关。 - 第二个错误来源于
Safebrowsing结构体里的finder字段包含了没有Send约束的闭包:你代码里的dyn for<'r> Fn(&'r [u8]) -> Option<usize>没有加Send约束,导致&mut self跨await时也不满足Send要求。
可行解决方案
方案一(更推荐,性能更好):提前提取数据,不持有锁执行异步操作
你完全不需要在执行耗时的网络请求时持有锁,只要提前把需要的链接提取出来,立刻释放锁之后再执行异步请求即可,修改后的代码如下:
调用侧调整:
// 提前提取链接,锁只会持有极短时间 let links = { let safebrowsing = self.safebrowsing.lock().unwrap(); safebrowsing.finder.links(&message.content).collect::<Vec<_>>() }; // 此时锁已经释放,直接执行异步网络请求 for link in links { match reqwest::get("url").await { Ok(response) => { println!( "{}", response.text().await.expect("response has no content") ); } Err(_) => { println!("Unable to get safebrowsing-response") } } }
结构体定义调整:
给finder字段的闭包加上Send约束,示例如下:
pub struct Safebrowsing { // 给dyn Fn加上 + Send 约束 finder: Finder<dyn for<'r> Fn(&'r [u8]) -> Option<usize> + Send + 'static>, // 其余字段不变 }
构造Finder实例时确保传入的闭包满足Send要求即可。
方案二:改用异步互斥锁
如果你确实有场景需要跨await持有锁,可以把标准库的Mutex替换为异步 runtime 提供的异步互斥锁,比如tokio::sync::Mutex,它的锁守卫实现了Send,可以安全跨await持有:
依赖调整:
确保你引入了tokio的sync特性:
tokio = { version = "1.0", features = ["sync"] }
字段类型调整:
把self.safebrowsing的类型从std::sync::Mutex<Safebrowsing>改为tokio::sync::Mutex<Safebrowsing>。
调用侧调整:
// 异步加锁 let mut safebrowsing = self.safebrowsing.lock().await; safebrowsing.is_safe(&message.content).await;
同时也要按照方案一的要求给finder的闭包加上Send约束才能解决第二个错误。
内容的提问来源于stack exchange,提问作者ExceptionUser
相关产品推荐
相关产品推荐

