如何从线程返回包含 Rc 的类型?

use*_*968 3 multithreading reference-counting rust

我正在生成一个线程,该线程构建本质上是一棵树,稍后需要它,因此可以在线程外进行计算。举个例子,考虑一个Foo { inner: HashMap<Bar, Rc<FooBar>> }. 线程的闭包不会捕获周围环境中的任何内容。

问题在于,它Rc构成了整个类型!Send。这会阻止Foo返回到生成线程。有效地,Rc“毒药” Foo

我不明白为什么由生成的线程创建并返回的类型仍然需要是Send:它强制所有内部类型(在本例中是Rcin the HashMap、 in Foo、 to be Send)。从它强制生成的线程始终使用原子计数,而实际上两个线程同时访问引用计数器的可能性为零。

Rc有没有办法从线程返回(而不是共享!)包含 an 的类型?我是否遗漏了一些理解Send

tre*_*tcl 5

[T]实际上,两个线程同时访问引用计数器的可能性为零。

编译器无法理解这一点,所以你必须说“不,真的,相信我,我知道它看起来很狡猾,但实际上是安全的。” 这就是unsafe目的。

它应该像这样简单

unsafe impl Send for Foo {}
Run Code Online (Sandbox Code Playgroud)

来自诺米康

  • Rc不是发送或同步(因为引用计数是共享的且不同步)。

Rc它本身并不安全Send,因为Rc暴露了一个如果在线程之间发送则不安全的接口。但是Foo,正如您所描述的,公开了一个在线程之间发送并非unsafe impl不安全的接口,因此只需使用它即可。

也就是说,假设接口在线程之间发送是安全的。Foo例如,不能有克隆并返回内部的方法Rc(因为您可以使用它通过有效地将裸机“走私”Rc到另一个线程中Foo)。

如果发送Foo并不总是安全的,但您碰巧知道某些特定的 Foo发送是安全的,更好的方法是将其暂时包装在 类型中Send,正如Shepmaster 的答案所建议的那样。


She*_*ter 5

Rust 语言对线程结束时不再有任何引用这一事实一无所知。事实上,它也不知道线程“开始”或“结束”的任何信息。

\n\n

从语言的角度来看,其中一种可能是Rc仍拥有其中一个克隆。一旦类型出现在线程上,事情就结束了。

\n\n
\n\n

当你有一些程序员知道但编译器不知道的东西时,就是这样的情况unsafe代码的情况。

\n\n

我不是 100% 确定,但是,据我所知,只要所有互连Rc的都在同一个线程上,它实际上是安全的。这应该从线程返回值

\n\n
use std::{rc::Rc, thread};\n\ntype MyTreeThing = Rc<i32>;\nstruct ReturningAnRcAcrossThreadsIsSafe(MyTreeThing);\n\n// I copied this from Stack Overflow without reading the text and\n// stating why I think this code is actually safe.\nunsafe impl Send for ReturningAnRcAcrossThreadsIsSafe {}\n\nfn main() {\n    let t = thread::spawn(|| ReturningAnRcAcrossThreadsIsSafe(Rc::new(42)));\n\n    let a = t.join().unwrap().0;\n\n    println!("{}", a);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个答案与trentcl 的答案不同,因为不安全代码的范围要小得多 \xe2\x80\x94 只有跨线程返回的值才被它覆盖。他们的答案将整个MyTreeThing类型标记为可以安全地跨线程发送,无论上下文如何。这很好,只要您注意了解所有相关的Rcs 总是在线程之间批量移动。

\n\n

也可以看看:

\n\n\n