`RefCell<std::string::String>` 无法在线程之间安全共享?

rol*_*lls 10 rust hyper

这是如何在 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")
            }
        }))
    });

Run Code Online (Sandbox Code Playgroud)

不幸的是,我得到RefCell<std::string::String> cannot be shared between threads safely

在此输入图像描述

C. *_*ang 6

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

下面是将 Arc<Mutex<>> 移动到闭包中的示例:

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);
    };
}
Run Code Online (Sandbox Code Playgroud)


Sve*_*rev 5

您的错误消息中的第一个错误是Sync未针对RefCell<String>. 这是设计使然Sync,如rustdoc所述:

\n
\n

非同步类型是那些以非线程安全形式具有 \xe2\x80\x9cinterior mutability\xe2\x80\x9d 的类型,例如 Cell 和 RefCell。即使通过不可变的共享引用,这些类型也允许\n改变其内容。例如,Cell 上的 set 方法采用 &self,因此\n只需要共享引用 &Cell。该方法不执行同步,因此 Cell 无法同步。

\n
\n

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

\n

但如果你把它包裹起来呢Arc?好吧,rustdoc又说得很清楚了:

\n
\n

只要 T 实现发送\n和同步,Arc 就会实现发送和同步。为什么\xe2\x80\x99 不能将非线程安全类型 T 放入 Arc 中以\n使其成为线程安全的?乍一看这可能有点违反直觉:\n毕竟,\xe2\x80\x99 不是 Arc 线程安全的重点吗?关键在于:\nArc 使拥有相同数据的多个所有权成为线程安全,但它并没有\xe2\x80\x99 为其数据添加线程安全性。考虑\nArc<RefCell>。RefCell 是\xe2\x80\x99t 同步的,如果 Arc 始终是发送的,\nArc<RefCell> 也会是同步的。但是我们\xe2\x80\x99d 有一个问题:\nRefCell 不是线程安全的;它使用非原子操作来跟踪借用计数\n。

\n

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

\n
\n

Arc<T>不会的,Sync除非TSync因为同样的原因。鉴于此,您可能应该使用 std/tokioMutex而不是RefCell

\n