gpt4 book ai didi

rust - 如果strong_count 为1 且weak_count 为0,那么包含 `Send` 的 `Rc` 结构是否安全?

转载 作者:行者123 更新时间:2023-12-03 11:47:11 30 4
gpt4 key购买 nike

我有一个不是 Send 的结构体因为它包含 Rc .可以说 Arc开销太大,所以我想继续使用 Rc .我还是想偶尔Send线程之间的这个结构,但只有当我可以验证 Rc有 strong_count 1 和 weak_count 0。

这是我想到的(希望是安全的)抽象:

mod my_struct {
use std::rc::Rc;

#[derive(Debug)]
pub struct MyStruct {
reference_counted: Rc<String>,
// more fields...
}

impl MyStruct {
pub fn new() -> Self {
MyStruct {
reference_counted: Rc::new("test".to_string())
}
}

pub fn pack_for_sending(self) -> Result<Sendable, Self> {
if Rc::strong_count(&self.reference_counted) == 1 &&
Rc::weak_count(&self.reference_counted) == 0
{
Ok(Sendable(self))
} else {
Err(self)
}
}

// There are more methods, some may clone the `Rc`!
}

/// `Send`able wrapper for `MyStruct` that does not allow you to access it,
/// only unpack it.
pub struct Sendable(MyStruct);

// Safety: `MyStruct` is not `Send` because of `Rc`. `Sendable` can be
// only created when the `Rc` has strong count 1 and weak count 0.
unsafe impl Send for Sendable {}

impl Sendable {
/// Retrieve the inner `MyStruct`, making it not-sendable again.
pub fn unpack(self) -> MyStruct {
self.0
}
}
}

use crate::my_struct::MyStruct;

fn main() {
let handle = std::thread::spawn(|| {
let my_struct = MyStruct::new();
dbg!(&my_struct);

// Do something with `my_struct`, but at the end the inner `Rc` should
// not be shared with anybody.

my_struct.pack_for_sending().expect("Some Rc was still shared!")
});

let my_struct = handle.join().unwrap().unpack();
dbg!(&my_struct);
}

我在 Rust playground 上做了一个演示.

有用。我的问题是,它真的安全吗?

我知道 Rc仅由一个单一的onwer拥有,没有人可以在我的手中更改它,因为其他线程无法访问它,我们将其包装为 Sendable这不允许访问包含的值。

但在某个疯狂的世界 Rc例如可以在内部使用线程本地存储,这将是不安全的......那么是否有一些保证我可以做到这一点?

我知道我必须非常小心,不要为 MyStruct 引入一些额外的原因。不是 Send .

最佳答案


有多个点需要验证才能发送Rc跨线程:

  • 不能有其他句柄( RcWeak )共享所有权。
  • Rc的内容必须是 Send .
  • Rc的实现必须使用线程安全策略。

  • 让我们按顺序回顾它们!
    保证没有混叠
    虽然你的算法——你自己检查计数——现在有效,但最好直接询问 Rc是否别名。
    fn is_aliased<T>(t: &mut Rc<T>) -> bool { Rc::get_mut(t).is_some() }
    get_mut 的实现如果 Rc 的实现将进行调整以你没有预见到的方式改变。
    可发送的内容
    当您执行 MyStruct 时当前看跌期权 String (即 Send )变成 Rc ,明天可能会更改为 Rc<str> ,然后所有的赌注都关闭了。
    因此,sendable 检查需要在 Rc 处实现。级别本身,否则您需要审核任何更改 Rc持有。
    fn sendable<T: Send>(mut t: Rc<T>) -> Result<Rc<T>, ...> {
    if !is_aliased(&mut t) {
    Ok(t)
    } else {
    ...
    }
    }
    线程安全 Rc内部结构
    而且……不能保证。
    Rc不是 Send ,其实现可以通过多种方式进行优化:
  • 可以使用线程局部区域来分配整个内存。
  • 计数器可以使用线程局部区域单独分配,以便无缝地转换为/从 Box .
  • ...

  • 目前情况并非如此,AFAIK,但是 API 允许这样做,因此下一个版本肯定可以利用这一点。

    你该怎么办?
    您可以制作 pack_for_sending unsafe ,并尽职尽责地记录所有依赖的假设——我建议使用 get_mut删除其中之一。然后,在 Rust 的每个新版本中,您必须仔细检查每个假设以确保您的使用仍然安全。
    或者,如果你不介意进行分配,你可以写一个转换为 Arc<T>你自己(见 Playground):
    fn into_arc(this: Rc) -> Result {
    Rc::try_unwrap(this).map(|t| Arc::new(t))
    }
    或者,您可以编写 RFC 建议 Rc <-> Arc转换!
    API 将是:
    fn Rc<T: Send>::into_arc(this: Self) -> Result<Arc<T>, Rc<T>>

    fn Arc<T>::into_rc(this: Self) -> Result<Rc<T>, Arc<T>>
    这可以在 std 内非常有效地完成,并且可能对其他人有用。
    然后,您将从 MyStruct 转换至 MySendableStruct ,只需移动字段并转换 RcArc随时发送到另一个线程,然后转换回 MyStruct .
    你不需要任何 unsafe ...

    关于rust - 如果strong_count 为1 且weak_count 为0,那么包含 `Send` 的 `Rc` 结构是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65270090/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com