在Rust documentation的以下代码中,它讨论了Rust中的并发线程。
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());
}
我仍然无法理解句柄的for循环的概念
也就是
for handle in handles {
handle.join().unwrap();
}
文档中说 “我们在每个句柄上调用join以确保所有线程都完成。“ 在一个实验中,我注解掉了循环的句柄,得到的输出是8而不是10。当我将循环改为1000时,当句柄循环被注解时,我得到的输出是999。这是怎么回事?8和999是如何变成输出的?
**编辑:**我发现this documentation涉及到处理和线程的一般概念。
[1]: https://doc.rust-lang.org/book/ch16-03-shared-state.html
2条答案
按热度按时间jpfvwuh41#
我仍然无法理解句柄的for循环的概念
JoinHandle::join
阻塞,直到相应的线程执行完毕(因此函数已经到达它的末尾),这是它的大部分内容(有用的是,它还生成线程的函数返回的任何内容)。当我把循环改为1000时,当句柄循环被注解时,我得到了999。这里发生了什么?8 & 999怎么会变成输出呢?
如果不对线程执行
join
,则线程与主线程之间会发生竞争(main
函数)。1.创建所有线程
1.将每个线程添加到向量
1.锁定柜台
这将根据系统负载和操作系统调度细节而改变,尽管主线程的大部分延迟将是......产生更多的线程(与产生一个线程相比,获取一个锁和增加一个数字是便宜的),这就是为什么大多数线程在打印结果时已经完成。如果您增加每个线程的工作量,或者改变线程产生的方式,您将看到不同的竞争。
yhuiod9q2#
join()所做的只是等待线程完成,它要求操作系统阻塞主线程(调用
join()
的线程),直到被加入的线程完成,并收集其状态。在调用
join()
之前,你只知道你已经把线程交给了操作系统。你不知道在代码的后面,它是否已经开始,正在运行,已经完成,被操作系统杀死,或者已经崩溃(),等等。在调用
join()
之前,您对共享counter
的唯一了解是,您可以在一致的状态下读取它(多亏了互斥体),也就是说,不会冒主线程和另一个线程之间可能正在递增它的竞争条件的风险。