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

Rust中Arc<RefCell<String>>报无法安全跨线程共享错误怎么解决?

问题原因

Arc<RefCell<T>>仅适用于单线程下的共享可变访问,RefCell没有实现Sync trait,无法安全跨线程共享。而hyper的请求处理器默认运行在多线程异步 runtime 上,会在多个线程中调用你写的闭包访问变量,因此触发编译错误。
另外你原有代码还存在两处隐含问题:

  1. 直接将外层变量move到闭包中,会导致变量所有权被第一次调用的闭包拿走,后续调用时会找不到变量
  2. 直接对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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 05:24:02