【问题标题】:`RefCell<std::string::String>` cannot be shared between threads safely?`RefCell<std::string::String>` 不能在线程之间安全共享?
【发布时间】:2021-06-07 18:57:20
【问题描述】:

这是How to re-use a value from the outer scope inside a closure in Rust?的延续,为了更好的展示,打开了新Q。

// main.rs

    // The value will be modified eventually inside `main` 
    // and a http request should respond with whatever "current" value it holds.
    let mut test_for_closure :Arc<RefCell<String>> = Arc::new(RefCell::from("Foo".to_string()));

// ...

    // Handler for HTTP requests
    // From https://docs.rs/hyper/0.14.8/hyper/service/fn.service_fn.html
    let make_svc = make_service_fn(|_conn| async {
        Ok::<_, Infallible>(service_fn(|req: Request<Body>| async move {
            if req.version() == Version::HTTP_11 {
                let foo:String = *test_for_closure.borrow();
                Ok(Response::new(Body::from(foo.as_str())))
            } else {
                Err("not HTTP/1.1, abort connection")
            }
        }))
    });

不幸的是,我收到了RefCell&lt;std::string::String&gt; cannot be shared between threads safely

【问题讨论】:

  • 你克隆你的 Arc 吗?这部分在您的代码中不可见。见cloning Arc references
  • 请将错误复制为问题中的文本。不要将它们作为屏幕截图包含在内:图像可能难以阅读,具体取决于查看者的屏幕分辨率,并且其他有类似问题的人无法搜索。

标签: rust hyper


【解决方案1】:

RefCell 仅适用于单线程。您将需要使用类似但适用于多个线程的 Mutex。您可以在此处阅读有关 Mutex 的更多信息:https://doc.rust-lang.org/std/sync/struct.Mutex.html

这是一个将 Arc> 移动到闭包中的示例:

use std::sync::{Arc, Mutex};

fn main() {
    let mut test: Arc<Mutex<String>> = Arc::new(Mutex::from("Foo".to_string()));

    let mut test_for_closure = Arc::clone(&test);
    let closure = || async move {
        // lock it so it cant be used in other threads
        let foo = test_for_closure.lock().unwrap();
        println!("{}", foo);
    };
}

【讨论】:

【解决方案2】:

您的错误消息中的第一个错误是Sync 未针对RefCell&lt;String&gt; 实现。这是设计使然,正如Syncrustdoc 所述:

不同步的类型是那些在 非线程安全的形式,例如 Cell 和 RefCell。这些类型允许 即使通过不可变的、共享的方式改变它们的内容 参考。例如,Cell 上的 set 方法采用 &self,所以它 只需要一个共享引用 &Cell。该方法不执行 同步,因此 Cell 不能同步。

因此,在线程之间共享 RefCell 是不安全的,因为您可能会通过常规的共享引用导致数据竞争。

但是如果你把它包裹在 Arc 中呢?嗯,rustdoc 又很清楚了:

只要 T 实现了 Send,Arc 就会实现 Send 和 Sync 和同步。为什么不能将非线程安全类型 T 放在 Arc 中 让它线程安全?一开始这可能有点违反直觉: 毕竟,不是 Arc 线程安全的重点吗?关键是这样的: Arc 使线程安全地拥有相同的多个所有权 数据,但它不会为其数据添加线程安全性。考虑 弧。 RefCell 不是 Sync,如果 Arc 总是 Send, Arc 也是如此。但是我们会遇到一个问题: RefCell 不是线程安全的;它跟踪借款数量 使用非原子操作。

最后,这意味着您可能需要将 Arc 与某种类型配对 std::sync 类型的,通常是 Mutex。

Arc&lt;T&gt; 不会是 Sync,除非 TSync,原因相同。鉴于此,您可能应该使用 std/tokio Mutex 而不是 RefCell

【讨论】:

  • 非常感谢您的详细解释!这对我来说现在更有意义了。我尝试使用互斥锁,它更进一步,但出现了不同的错误。如果你想看看,我在这里做了一个跟进 Q:stackoverflow.com/questions/67879442/…
猜你喜欢
  • 1970-01-01
  • 2023-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-20
  • 2017-10-02
  • 1970-01-01
相关资源
最近更新 更多