Rust中Arc<RefCell<String>>报无法安全跨线程共享错误怎么解决?
问题原因
Arc<RefCell<T>>仅适用于单线程下的共享可变访问,RefCell没有实现Sync trait,无法安全跨线程共享。而hyper的请求处理器默认运行在多线程异步 runtime 上,会在多个线程中调用你写的闭包访问变量,因此触发编译错误。
另外你原有代码还存在两处隐含问题:
- 直接将外层变量move到闭包中,会导致变量所有权被第一次调用的闭包拿走,后续调用时会找不到变量
- 直接对
RefCell的借用结果解引用赋值给String类型,会触发「无法移出借用内容」的编译错误
解决方案
将RefCell替换为异步锁tokio::sync::Mutex,同时每次闭包捕获变量前先克隆Arc(仅增加引用计数,性能开销极低),代码修改如下:
第一步:导入依赖
use std::sync::Arc; use tokio::sync::Mutex; // 其他hyper相关导入保持不变
第二步:修改变量定义
Arc<Mutex<T>内部可变性不需要外层mut修饰:
// 该值最终会在`main`中被修改,HTTP请求需要返回它的当前值 let test_for_closure: Arc<Mutex<String>> = Arc::new(Mutex::new("Foo".to_string()));
第三步:修改服务处理器代码
// 先克隆一份Arc,转移到外层make_service_fn闭包 let test_clone = Arc::clone(&test_for_closure); let make_svc = make_service_fn(move |_conn| { // 每个新连接生成一份新的Arc克隆,转移到service_fn闭包 let inner_clone = Arc::clone(&test_clone); async move { Ok::<_, Infallible>(service_fn(move |req: Request<Body>| { // 每个请求生成一份新的Arc克隆,转移到请求处理异步块 let req_clone = Arc::clone(&inner_clone); async move { if req.version() == Version::HTTP_11 { // 异步加锁,等待获取访问权限 let foo = req_clone.lock().await; // 直接用借用的字符串构造返回体,不需要额外克隆String Ok(Response::new(Body::from(&*foo))) } else { Err("非HTTP/1.1请求,中断连接") } } })) } });
注意事项
如果你的项目确定使用单线程异步runtime,也可以继续使用RefCell,但实际生产环境下hyper默认使用多线程runtime,优先使用tokio::sync::Mutex不会阻塞工作线程,性能表现更好。如果是计算密集型场景锁持有时间很短,也可以替换为std::sync::Mutex,调用lock().unwrap()即可,不需要await。
内容的提问来源于stack exchange,提问作者rollingBalls
相关产品推荐
相关产品推荐

