我是Rust和线程的新手,我正在尝试打印一个数字,同时在另一个线程中添加它。我怎么能做到这一点?
use std::thread;
use std::time::Duration;
fn main() {
let mut num = 5;
thread::spawn(move || {
loop {
num += 1;
thread::sleep(Duration::from_secs(10));
}
});
output(num);
}
fn output(num: i32) {
loop {
println!("{:?}", num);
thread::sleep(Duration::from_secs(5));
}
}
上面的代码不起作用:它总是只打印5
,就像数字永远不会增加一样。
答案 0 :(得分:8)
请阅读"Concurrency" chapter, "Safe Shared Mutable State" section of The Rust Book,它会详细说明如何执行此操作。
简而言之:
datasource
已复制,因此num
并且该主题在该号码的不同副本上运行。如果output()
不可复制,Rust编译器将无法编译并出现错误。Arc
( a tomic r eference- 中c 安装变量)num
内的变量,因此需要将其放在Mutex
或RwLock
中。您使用Arc
方法从.lock()
中获取可变引用。该方法将确保在该可变引用的生命周期内对整个过程进行独占访问。Mutex
您可能还想阅读:
use std::thread;
use std::time::Duration;
use std::sync::{Arc, Mutex};
fn main(){
let num = Arc::new(Mutex::new(5));
// allow `num` to be shared across threads (Arc) and modified
// (Mutex) safely without a data race.
let num_clone = num.clone();
// create a cloned reference before moving `num` into the thread.
thread::spawn(move || {
loop {
*num.lock().unwrap() += 1;
// modify the number.
thread::sleep(Duration::from_secs(10));
}
});
output(num_clone);
}
fn output(num: Arc<Mutex<i32>>){
loop {
println!("{:?}", *num.lock().unwrap());
// read the number.
// - lock(): obtains a mutable reference; may fail,
// thus return a Result
// - unwrap(): ignore the error and get the real
// reference / cause panic on error.
thread::sleep(Duration::from_secs(5));
}
}
代替Arc<Mutex<i32>>
)Arc<i32>
代替Arc<Mutex<i32>>
)答案 1 :(得分:2)
另一个答案可以解决任何类型的问题,但是作为pnkfelix observes,原子包装器类型是另一种适用于i32
特定情况的解决方案。
从Rust 1.0开始,您可以使用AtomicBool
,AtomicPtr<T>
,AtomicIsize
和AtomicUsize
来同步对bool
,{{1}的多线程访问},*mut T
和isize
值。在Rust 1.34中,几个新的usize
类型已经稳定,包括Atomic
。 (请查看AtomicI32
文档以获取当前列表。)
使用原子类型最有可能比锁定std::sync::atomic
或Mutex
更有效率,但是需要更多地注意内存排序的底层细节。如果您的线程共享的数据超出了标准原子类型之一的容纳量,则您可能希望使用RwLock
而不是多个Mutex
。
也就是说,这是kennytm使用Atomic
而不是AtomicI32
的答案的版本:
Mutex<i32>
共享所有权仍然需要 use std::sync::{
atomic::{AtomicI32, Ordering},
Arc,
};
use std::thread;
use std::time::Duration;
fn main() {
let num = Arc::new(AtomicI32::new(5));
let num_clone = num.clone();
thread::spawn(move || loop {
num.fetch_add(1, Ordering::SeqCst);
thread::sleep(Duration::from_secs(10));
});
output(num_clone);
}
fn output(num: Arc<AtomicI32>) {
loop {
println!("{:?}", num.load(Ordering::SeqCst));
thread::sleep(Duration::from_secs(5));
}
}
(但请参见How can I pass a reference to a stack variable to a thread?)。
选择正确的存储器Arc
并非易事。 Ordering
是最保守的选择,但是如果仅共享一个内存地址,则SeqCst
也应该起作用。有关更多信息,请参见下面的链接。