前言
在Rust语言中,所有权大多数情况下是明确的,对于一个给定的值,你可以准确地判断出,哪个变量拥有它。但是也存在一些情况,单个值可能同时被多个所有者持有。
请看下面的例子:
` fn process_files_in_parallel(filenames: Vec<string>, glossary: &GigabyteMap)
-> io::Result<()>
{
...
for worklist in worklists
{
thread_handlers.push(
spawn(move || process_files(worklist, glossary)
);
}
....
}
`
上面的例子中,拟采用多个进程,并发地处理文件。但是处理文件需要一个消耗巨大内存的数据结构, glossary,可能数据结构大小超过GB,这种情况下,拷贝多个副本给不同线程使用无疑是无法承受的。但是上面的代码,存在一个问题,即glossary的生命周期。
因为glossary并不能保证在所有的子进程完成任务之前不被销毁,如果被销毁,会导致子进程无法正常完成任务。
Rc and Arc
Rust提供了一个名为Rc
那Arc是干啥的呢?Arc具备Rc的一切特征,那是Rc不是线程安全的,所以Rust又提供了一个Arc
那看起来是Arc是更强大的Rc,能够保证安全地用在并发的场景,那为什么不干脆消灭掉Rc,统统只使用Arc呢?
没有免费的午餐,Arc更强大,但是这种强大,需要付出一些性能开销才能做到,如果是单线程条件下,我们其实不需要付出这种开销。因此
- 单线程条件下,可以使用Rc
- 多线程并发条件下,建议使用Arc
use std::thread;
use std::time::Duration;
use std::sync::Arc;
fn main() {
let foo = Arc::new(vec![0]);
for _ in 0..10 {
let bar = Arc::clone(&foo);
thread::spawn(move || {
thread::sleep(Duration::from_millis(200));
println!("{:?}", &bar);
});
}
println!("{:?}", foo);
}
上面的用法中, Arc::clone 并不是复制了一份vec,而是仅仅增减了一个引用计数,这是多个线程之间共享数据的一个方法。
回到我们最开始的问题,巨大的数据结构,多个线程之间共享:
fn process_files_in_parallel(filenames: Vec<String>, glossary: Arc<GigabyteMap>)
-> io::Result<()>
{
....
for worklist in worklists
{
let glossary_for_child = glossary.clone() ;
thread_handlers.push(
spawn(move || process_files(worklist, &glossary_for_child))
);
}
....
}