Rust从入门到实战系列一百九十四:原子引用计数 Arc<T>

所幸 Arc 正是这么一个类似 Rc 并可以安全的用于并发环境的类型。字母 ”a” 代表 原子性(atomic),
所以这是一个 原子引用计数(atomically reference counted)类型。原子性是另一类这里还未涉及到的
并发原语:请查看标准库中 [std:: sync::atomic][atomic] 的文档来获取更多细节。目前我们只需要知道
原子类就像基本类型一样可以安全的在线程间共享。
你可能会好奇为什么不是所有的原始类型都是原子性的?为什么不是所有标准库中的类型都默认使用
Arc 实现?原因在于线程安全带有性能惩罚,我们希望只在必要时才为此买单。如果只是在单线程中
对值进行操作,原子性提供的保证并无必要,代码可以因此运行的更快。
回到之前的例子:Arc 和 Rc 有着相同的 API,所以修改程序中的 use 行和 new 调用。示例 16-15
中的代码最终可以编译和运行:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0…10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!(“Result: {}”, *counter.lock().unwrap());
}
示例 16-15: 使用 Arc 包装一个 Mutex 能够实现在多线程之间共享所有权
这会打印出:
Result: 10
成功了!我们从 0 数到了 10,这可能并不是很显眼,不过一路上我们确实学习了很多关于 Mutex 和
线程安全的内容!这个例子中构建的结构可以用于比增加计数更为复杂的操作。使用这个策略,可将计
算分成独立的部分,分散到多个线程中,接着使用 Mutex 使用各自的结算结果更新最终的结果。
RefCell∕Rc 与 Mutex∕Arc 的相似性
你可能注意到了,因为 counter 是不可变的,不过可以获取其内部值的可变引用;这意味着 Mutex
提供了内部可变性,就像 Cell 系列类型那样。正如第十五章中使用 RefCell 可以改变 Rc 中的内
容那样,同样的可以使用 Mutex 来改变 Arc 中的内容。
另一个值得注意的细节是 Rust 不能避免使用 Mutex 的全部逻辑错误。回忆一下第十五章使用 Rc
就有造成引用循环的风险,这时两个 Rc 值相互引用,造成内存泄漏。同理,Mutex 也有造成 死
锁(deadlock)的风险。这发生于当一个操作需要锁住两个资源而两个线程各持一个锁,这会造成它们
永远相互等待。如果你对这个主题感兴趣,尝试编写一个带有死锁的 Rust 程序,接着研究任何其他语言
中使用互斥器的死锁规避策略并尝试在 Rust 中实现他们。标准库中 Mutex 和 MutexGuard 的 API
文档会提供有用的信息。
接下来,为了丰富本章的内容,让我们讨论一下 Send和 Sync trait 以及如何对自定义类型使用他们。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值