新手写了一个晚上没写出来, 求帮忙写个功能?
新手写了一个晚上没写出来, 各种报错, baidu google 翻遍了, 求帮忙写个参考一下。有一个向量 let data: Vec<i32> = (0..50).collect();
需要开三个thread来修改data每个thread获取data的4个元素, 原地修改元素// 0, 1, 2, 3 -> 1, 2, 3, 41号thread +1
// 4, 5, 6, 7 -> 6, 7, 8, 92号thread +2
// 8, 9, 10, 11 -> 11, 12, 13, 143号thread +3
Implement step by step
先使用Vec<i32>来存放数据,然后创建三个线程分别对数据进行修改,代码大概是下面这样:
use std::thread;
fn main() {
let mut data = (0..50).collect::<Vec<i32>>();
let mut handles = vec![];
for _ in [0,4,8] {
let handle = thread::spawn(|| {
//没有实现完整逻辑
data[0] = 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", data);
}
运行一下有下面这些错误:
error[E0499]: cannot borrow `data` as mutable more than once at a time
--> src/main.rs:14:36
|
14 | let handle = thread::spawn(|| {
| - ^^ `data` was mutably borrowed here in the previous iteration of the loop
| ______________________|
| |
15 | | //没有实现完整逻辑
16 | | data[0] = 1;
| | ---- borrows occur due to use of `data` in closure
17 | | });
| |__________- argument requires that `data` is borrowed for `'static`
上面因为在 for _ in [0,4,8]..... 这个循环中创建的线程中的闭包对data的可变引用,3次可变引用是不允许的
error[E0373]: closure may outlive the current function, but it borrows `data`, which is owned by the current function
--> src/main.rs:14:36
|
14 | let handle = thread::spawn(|| {
| ^^ may outlive borrowed value `data`
15 | //没有实现完整逻辑
16 | data[0] = 1;
| ---- `data` is borrowed here
|
note: function requires argument type to outlive `'static`
--> src/main.rs:14:22
|
14 | let handle = thread::spawn(|| {
| ______________________^
15 | | //没有实现完整逻辑
16 | | data[0] = 1;
17 | | });
| |__________^
help: to force the closure to take ownership of `data` (and any other referenced variables), use the `move` keyword
|
14 | let handle = thread::spawn(move || {
| ++++
上面编译器检测到线程中的闭包对可能在main函数外存活,而对data的可变引用此时还存在, 如果此时data不存在就会出问题(按理说我们的线程join之后不该有这个错误提示的😂)。然后提出的提示是使用move把data的所有权转给闭包
error[E0502]: cannot borrow `data` as immutable because it is also borrowed as mutable
--> src/main.rs:25:22
|
14 | let handle = thread::spawn(|| {
| - -- mutable borrow occurs here
| ______________________|
| |
15 | | //没有实现完整逻辑
16 | | data[0] = 1;
| | ---- first borrow occurs due to use of `data` in closure
17 | | });
| |__________- argument requires that `data` is borrowed for `'static`
...
25 | println!("{:?}", data);
| ^^^^ immutable borrow occurs here
|
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
上面线程中的闭包是对data的可变引用而println是对data的不变引用
上面的错误提示中使用move把data的所有权转给一个闭包使用是不行的,我们要把data给3个线程的闭包使用而不是一个;所以要想办法把data分给3个线程使用,这里想到的是Rc<T> 引用计数(reference counting),当Rc类型变量的引用计数不为0时这个变量不会被释放,使用Rc::clone使某个变量的引用计数加一;下面代码先使data的引用计数加一,move语意的闭包会使线程执行完毕后data的引用计数减一
fn main() {
//下面两种效果一样
let r_c = Rc::new(0);
//使用方法
let _new_rc = r_c.clone();
//使用完全限定
let _new_rc_1 = Rc::clone(&r_c);
}
use std::thread;
use std::rc::Rc;
fn main() {
let r_c = Rc::new((0..50).collect::<Vec<i32>>());
let mut handles = vec![];
for _ in [0,4,8] {
let data = Rc::clone(&r_c);
let handle = thread::spawn(move || {
//没有实现完整逻辑
data[0] = 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", r_c);
}
运行一下得到下面错误:
error[E0277]: `Rc<Vec<i32>>` cannot be sent between threads safely
--> src/main.rs:15:36
|
15 | let handle = thread::spawn(move || {
| ------------- ^------
| | |
| ______________________|_____________within this `[closure@src/main.rs:15:36: 15:43]`
| | |
| | required by a bound introduced by this call
16 | | //没有实现完整逻辑
17 | | data[0] = 1;
18 | | });
| |_________^ `Rc<Vec<i32>>` cannot be sent between threads safely
|
= help: within `[closure@src/main.rs:15:36: 15:43]`, the trait `Send` is not implemented for `Rc<Vec<i32>>`
note: required because it's used within this closure
--> src/main.rs:15:36
|
15 | let handle = thread::spawn(move || {
| ^^^^^^^
note: required by a bound in `spawn`
--> /rustc/84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc/library/std/src/thread/mod.rs:711:1
出现上面错误的原因是Rc<Vec<i32>>没有实现 Send
trait,Send trait 的作用是 保证实现了 Send trait 的类型值的所有权可以在线程间传送。几乎所有的 Rust类型都是Send的,不过有一些例外,Rc<T> 就是不能Send的,因为如果克隆了 Rc<T>的值并尝试将克隆的所有权转移到另一个线程,那么这两个线程都可能同时更新引用计数。为此,Rc<T> 被实现为用于单线程场景。而Arc(Atomically Reference Counted)实现了Send
trait,下面改为Arc
use std::thread;
use std::sync::Arc;
fn main() {
let r_c = Arc::new((0..50).collect::<Vec<i32>>());
let mut handles = vec![];
for _ in [0,4,8] {
let data = Arc::clone(&r_c);
let handle = thread::spawn(move || {
//没有实现完整逻辑
data[0] = 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", r_c);
}
执行一下得到的错误信息:
error[E0596]: cannot borrow data in an `Arc` as mutable
--> src/main.rs:17:13
|
17 | data[0] = 1;
| ^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Vec<i32>>`
因为Arc<T> 没有实现 DerefMut
trait 所以才会报错这个错误,怎么办? 最直接的想法就是为Arc实现DerefMut trait
。这里换一种思考能不能在Arc与Vec之间加一层?
想要在没有实现DerefMut trait 的情况下 改变Vec中的数据第一想到是RefCell<T>,但是RefCell<T> 不能用于多线程,而Mutex 却是一个线程安全的RefCell<T> ,看到Mutex]中有与Arc配合使用的例子,下面尝试一下
use std::thread;
use std::sync::{Arc,Mutex};
fn main() {
let r_c = Arc::new(Mutex::new((0..50).collect::<Vec<i32>>()));
let mut handles = vec![];
for _ in [0,4,8] {
let data = Arc::clone(&r_c);
let handle = thread::spawn(move || {
//没有实现完整逻辑
let mut m = data.lock().unwrap();
//let mut m = (*(data.deref())).lock().unwrap();
m[0] = 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", r_c);
}
值得注意的是 data的类型是Arc<Mutex<Vec<i32>>> 为什么可以调用Mutex的lock方法? 因为Arc实现了 Deref
trait,
impl<T> Deref for Arc<T>
where
T: ?Sized,
type Target = T
The resulting type after dereferencing.
fn deref(&self) -> &T
Dereferences the value.
所以编译器会自动对data进行解引用,data.lock() 等价于 (*(data.deref())).lock()
当获取锁后lock返回一个LockResult<MutexGuard<'_, T>>
pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>
所以unwrap解包后 m 的类型是 MutexGuard<'_, T>
同理因为MutexGuard 实现了 Deref
trait,
impl<T: ?Sized> Deref for MutexGuard<'_, T>
type Target = T
The resulting type after dereferencing.
fn deref(&self) -> &T
Dereferences the value.
编译器会自动解引用,所以可以写成 m[0] = 1;这种形式。
下面是完整实现
use std::thread;
use std::sync::{Arc,Mutex};
use std::collections::HashMap;
fn main() {
let r_c = Arc::new(Mutex::new((0..50).collect::<Vec<i32>>()));
let mut handles = vec![];
let index_value= HashMap::from([
(0,1),
(4,2),
(8,3),
]);
for (k,v) in index_value {
let c = Arc::clone(&r_c);
let handle = thread::spawn( move || {
modify_data(c,k,v);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", r_c);
}
//此方法借用了此问题的回答者 **`Fractal`** 的实现
fn modify_data(c: Arc<Mutex<Vec<i32>>>, start: usize, increment: i32) {
let mut data = c.lock().unwrap();
for i in start..start+4 {
data[i] += increment;
}
}
Arc 配合 RwLock 方式的实现
use std::thread;
use std::sync::{Arc, RwLock};
use std::collections::HashMap;
fn main() {
let c_lock = Arc::new(RwLock::new((0..50).collect()));
let lock = Arc::clone(&c_lock);
let mut handles = vec![];
let index_value= HashMap::from([
(0,1),
(4,2),
(8,3),
]);
for (k,v) in index_value {
let c = Arc::clone(&lock);
let handle = thread::spawn( move || {
modify_data(c,k,v);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("{:?}", c_lock);
}
//此方法借用了此问题的回答者 **`Fractal`** 的实现
fn modify_data(c: Arc<RwLock<Vec<i32>>>, start: usize, increment: i32) {
let mut data = c.write().unwrap();
for i in start..start+4 {
data[i] += increment;
}
}
参考
modify_data
方法借用了此问题的回答者 Fractal
的实现https://doc.rust-lang.org/std/sync/struct.Mutex.html#exampleshttps://kaisery.github.io/trpl-zh-cn/ch16-04-extensible-concu...
- 经过验证的有效解决办法
- 自己的经验指引,对解决问题有帮助
- 遵循 Markdown 语法排版,代码语义正确
- 询问内容细节或回复楼层
- 与题目无关的内容
- “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容