gpt4 book ai didi

rust - 在没有未定义行为的情况下,如何在 Rust 中的对等点之间共享不安全状态?

转载 作者:行者123 更新时间:2023-11-29 08:13:05 25 4
gpt4 key购买 nike

我有一个静态数组类型,它允许您在它保存的数据中创建多个只读“ View ”切片;但在 Drop 上,它断言!没有引用不再存在的数据的“挂起” View 。

看起来你可以通过向结构添加一个堆分配的整数来做到这一点,就像在不安全的指南中一样;像这样的东西:

extern crate libc;

use libc::{c_void, calloc, free, size_t};
use std::mem::size_of;

struct Foo {
count: *mut i32,
value: i32,
}

impl Foo {
fn new(parent: Option<&mut Foo>) -> Foo {
match parent {
Some(p) => {
unsafe {
let tmp = &mut *p.count;
*tmp += 1;
println!("Created a new record, the count is now: {}", *tmp);
}
return Foo {
value: 0,
count: p.count,
};
}
None => unsafe {
let counter = calloc(size_of::<i32> as size_t, 1 as size_t) as *mut i32;
println!("counter record: {}", *counter);
return Foo {
value: 0,
count: counter,
};
},
}
}

fn count(&self) -> i32 {
unsafe {
return *self.count;
}
}
}

drop 实现更新计数器的地方:

impl Drop for Foo {
fn drop(&mut self) {
unsafe {
let tmp = &mut *self.count;
*tmp -= 1;
println!("Dropped a record, the count is now: {}", *tmp);
if *tmp == -1 {
println!("counter record: {}", *self.count);
free(self.count as *mut c_void);
println!("The final record was dropped");
}
}
}
}

这段代码工作正常,测试:

fn main() {
let mut parent = Foo::new(None);
{
let child1: Foo;
let child2: Foo;
let child3: Foo;
let child4: Foo;
let child5: Foo;
{ child1 = Foo::new(Some(&mut parent)); }
{ child2 = Foo::new(Some(&mut parent)); }
{ child3 = Foo::new(Some(&mut parent)); }
{ child4 = Foo::new(Some(&mut parent)); }
{ child5 = Foo::new(Some(&mut parent)); }
assert!(parent.count() == 5);
}
assert!(parent.count() == 0);
}

产量:

counter record: 0x7f909f7fc010
Created a new record, the count is now: 1
Created a new record, the count is now: 2
Created a new record, the count is now: 3
Created a new record, the count is now: 4
Created a new record, the count is now: 5
Dropped a record, the count is now: 4
Dropped a record, the count is now: 3
Dropped a record, the count is now: 2
Dropped a record, the count is now: 1
Dropped a record, the count is now: 0
Dropped a record, the count is now: -1
counter record: 0x7f909f7fc010
The final record was dropped

这真的安全吗?

The unsafe guide说:

Raw pointers have much fewer guarantees than other pointer types offered by the Rust language and libraries. For example, they

... - are considered sendable (if their contents is considered sendable), so the compiler offers no assistance with ensuring their use is thread-safe; for example, one can concurrently access a *mut int from two threads without synchronization.

然而...

Going the opposite direction, from *const to a reference &, is not safe. A &T is always valid, and so, at a minimum, the raw pointer *const T has to be a valid to a valid instance of type T. Furthermore, the resulting pointer must satisfy the aliasing and mutability laws of references.

虽然上面的例子看起来“有效”,但它实际上是未定义的行为。在将 *const i32 转换为 &i32 以递增和递减引用计数时,&i32 必须满足指针别名规则;它不会,因为可以同时删除多个 Foo(可能,尽管在上面的示例中没有具体说明)。

您如何以不会导致未定义行为的方式“正确地”实现这种行为?

最佳答案

multiple Foos can be dropped at the same time

不,它们不能,至少,不能完全同时,因为析构函数是按顺序运行的。只要 Foo 对象保持在单个线程中,就永远不会有多个 &mut 借用到 .count 的时间点。 &mut 借用的两个地方都非常受限,并且在操作 count 时不会发生其他 Foo 操作。

然而,关键点是“保持单线程”。如果您在多个线程中有对象,您可以同时发生两个 Foo 操作:创建两个 Foo,然后将一个传递给另一个线程,现在每个线程都可以随时随地做任何想做的事,但它们都指向相同的数据。这是有问题的(和未定义的行为),原因有两个:

  1. 别名 &mut;这两个线程可能每个都在同一点执行带有 &mut 借用的地方之一。
  2. 数据竞赛;这两个线程在没有同步的情况下更新同一 block 内存。 (这一点很重要,之前的规则旨在作为一种工具来防止数据竞争等。)

解决这个问题的一种方法是使用marker traits 防止Foo 被提供给另一个线程。 .特别是,SendSync不会为原始指针自动实现,因此默认行为是您在这里想要的。

解决它的另一种方法,但允许在线程之间共享/发送是更改 count 以存储 AtomicIsize ,以避免数据竞争。您需要谨慎使用正确的操作来确保析构函数中的线程安全,否则您可能会在周围还有其他引用时释放内存。

关于rust - 在没有未定义行为的情况下,如何在 Rust 中的对等点之间共享不安全状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24904404/

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