gpt4 book ai didi

rust - 如何在线程之间传递带有 Rc 字段的结构(无需同时访问)

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

我有以下(简化的)代码生成几个线程,这些线程执行长而复杂的操作以构建 Transactions结构。这Transactions结构包含带有 Rc 的字段.在线程结束时,我想返回计算的 Transactions通过 mpsc::channel 结构到调用线程.

use std::thread;
use std::collections::HashMap;
use std::sync::mpsc::{channel, Sender};
use std::rc::Rc;

#[derive(Debug)]
struct Transaction {
id: String,
}

#[derive(Debug)]
struct Transactions {
list: Vec<Rc<Transaction>>,
index: HashMap<String, Rc<Transaction>>,
}

fn main() {

let (tx, rx) = channel();

for _ in 0..4 {
tx = Sender::clone(&tx);
thread::spawn(move || {
// complex and long computation to build a Transactions struct
let transactions = Transactions { list: Vec::new(), index: HashMap::new() };
tx.send(transactions).unwrap();
});
}

drop(tx);

for transactions in rx {
println!("Got: {:?}", transactions);
}

}

编译器提示 std::rc::Rc<Transaction>不能在线程之间安全发送,因为它没有实现 std::marker::Send特质。

error[E0277]: `std::rc::Rc<Transaction>` cannot be sent between threads safely
--> src/main.rs:23:5
|
23 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `std::rc::Rc<Transaction>` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `std::rc::Rc<Transaction>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<std::rc::Rc<Transaction>>`
= note: required because it appears within the type `alloc::raw_vec::RawVec<std::rc::Rc<Transaction>>`
= note: required because it appears within the type `std::vec::Vec<std::rc::Rc<Transaction>>`
= note: required because it appears within the type `Transactions`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::mpsc::Sender<Transactions>`
= note: required because it appears within the type `[closure@src/main.rs:23:19: 27:6 tx:std::sync::mpsc::Sender<Transactions>]`

我知道我可以替换 Rc通过 Arc , 但想知道是否有任何其他解决方案来避免使用 Arc 的性能损失,因为 Rc结构永远不会被两个线程同时访问。

最佳答案

只是不要这样做。

我正在发表评论,但我认为它实际上回答了您的问题并警告了其他人。

这看起来很不合理! Rc 不是关于管理访问,它是关于通过计算有多少引用是存活的来确保某些东西存在足够长的时间以在不同的“所有者”/“借用者”之间共享。如果在两个不同的线程中有两个 (Rc) 对同一个值的引用,缺乏原子性可能会导致两个线程在同一时间更改引用计数,这可能导致记录被弄脏,这可能会导致内存泄漏,或者更糟,过早地放弃分配和 UB。

这是因为递增共享变量的经典同步问题:

增加一个变量的步骤:

  1. 读取变量并将其存储在堆栈中。
  2. 将堆栈中的副本加 1。
  3. 将结果写回变量

一个线程就可以了,但让我们看看否则会发生什么:

多线程同步事件(线程 A 和 B)

  1. x=0
  2. A:将x读入xa(栈),xa = 0
  3. B:将x读入xb,xb =0
  4. A:增加xa,xa = 1
  5. A:将xa写入x,x =1
  6. B:增加xb,xb = 1
  7. B:将xb写入x,x = 1
  8. x 现在是 1

您现在已将 0 递增两次,结果为 1:差!

如果 x 是 Rc 的引用计数,它会认为只有一个引用是存活的。如果一个引用被删除,它会认为没有更多的引用存在并会删除该值,但实际上仍然有一个引用认为可以访问数据,因此是未定义的行为,因此非常糟糕!

与其他一切相比,Arc 的性能成本可以忽略不计,绝对值得使用。

关于rust - 如何在线程之间传递带有 Rc 字段的结构(无需同时访问),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65047755/

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