新手写了一个晚上没写出来, 各种报错, baidu google 翻遍了, 求帮忙写个参考一下。
有一个向量 let data: Vec<i32> = (0..50).collect();
需要开三个thread来修改data
每个thread获取data的4个元素, 原地修改元素
// 0, 1, 2, 3 -> 1, 2, 3, 4
1号thread +1
// 4, 5, 6, 7 -> 6, 7, 8, 9
2号thread +2
// 8, 9, 10, 11 -> 11, 12, 13, 14
3号thread +3
Implement step by step
先使用Vec<i32>来存放数据,然后创建三个线程分别对数据进行修改,代码大概是下面这样:
运行一下有下面这些错误:
上面因为在 for _ in [0,4,8]..... 这个循环中创建的线程中的闭包对data的可变引用,3次可变引用是不允许的
上面编译器检测到线程中的闭包对可能在main函数外存活,而对data的可变引用此时还存在, 如果此时data不存在就会出问题(按理说我们的线程join之后不该有这个错误提示的😂)。然后提出的提示是使用move把data的所有权转给闭包
上面线程中的闭包是对data的可变引用而println是对data的不变引用
上面的错误提示中使用move把data的所有权转给一个闭包使用是不行的,我们要把data给3个线程的闭包使用而不是一个;所以要想办法把data分给3个线程使用,这里想到的是Rc<T> 引用计数(reference counting),当Rc类型变量的引用计数不为0时这个变量不会被释放,使用Rc::clone使某个变量的引用计数加一;下面代码先使data的引用计数加一,move语意的闭包会使线程执行完毕后data的引用计数减一
运行一下得到下面错误:
出现上面错误的原因是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执行一下得到的错误信息:
因为Arc<T> 没有实现
DerefMut
trait 所以才会报错这个错误,怎么办?最直接的想法就是为Arc实现DerefMut trait
。这里换一种思考能不能在Arc与Vec之间加一层?
想要在没有实现DerefMut trait 的情况下 改变Vec中的数据第一想到是RefCell<T>,但是RefCell<T> 不能用于多线程,而Mutex 却是一个线程安全的RefCell<T> ,看到Mutex]中有与Arc配合使用的例子,下面尝试一下值得注意的是 data的类型是Arc<Mutex<Vec<i32>>> 为什么可以调用Mutex的lock方法? 因为Arc实现了
Deref
trait,所以编译器会自动对data进行解引用,data.lock() 等价于 (*(data.deref())).lock()
当获取锁后lock返回一个LockResult<MutexGuard<'_, T>>
所以unwrap解包后 m 的类型是 MutexGuard<'_, T>
同理因为MutexGuard 实现了
Deref
trait,编译器会自动解引用,所以可以写成 m[0] = 1;这种形式。
下面是完整实现
Arc 配合 RwLock 方式的实现
参考
modify_data
方法借用了此问题的回答者Fractal
的实现https://doc.rust-lang.org/std/sync/struct.Mutex.html#examples
https://kaisery.github.io/trpl-zh-cn/ch16-04-extensible-concu...