use std::sync::{Arc, Mutex, Once, ONCE_INIT};
use std::time::Duration;
use std::{mem, thread};
#[derive(Clone)]
struct SingletonReader {
// Since we will be used in many threads, we need to protect
// concurrent access
inner: Arc<Mutex<u8>>,
}
fn singleton() -> SingletonReader {
// Initialize it to a null value
static mut SINGLETON: *const SingletonReader = 0 as *const SingletonReader;
static ONCE: Once = ONCE_INIT;
unsafe {
ONCE.call_once(|| {
// Make it
let singleton = SingletonReader {
inner: Arc::new(Mutex::new(0)),
};
// Put it in the heap so it can outlive this call
SINGLETON = mem::transmute(Box::new(singleton));
});
// Now we give out a copy of the data that is safe to use concurrently.
(*SINGLETON).clone()
}
}
fn main() {
// Let's use the singleton in a few threads
let threads: Vec<_> = (0..10)
.map(|i| {
thread::spawn(move || {
thread::sleep(Duration::from_millis(i * 10));
let s = singleton();
let mut data = s.inner.lock().unwrap();
*data = i as u8;
})
})
.collect();
// And let's check the singleton every so often
for _ in 0u8..20 {
thread::sleep(Duration::from_millis(5));
let s = singleton();
let data = s.inner.lock().unwrap();
println!("It is: {}", *data);
}
for thread in threads.into_iter() {
thread.join().unwrap();
}
}
打印出:
It is: 0
It is: 1
It is: 1
It is: 2
It is: 2
It is: 3
It is: 3
It is: 4
It is: 4
It is: 5
It is: 5
It is: 6
It is: 6
It is: 7
It is: 7
It is: 8
It is: 8
It is: 9
It is: 9
It is: 9
1 回答
Non-answer answer
一般避免全球状态 . 相反,尽早在某处构建对象(可能在
main
中),然后将对该对象的可变引用传递到需要它的位置 . 这通常会使您的代码更易于推理,并且不需要向后弯曲 .在决定你想要全局可变变量之前,先在镜子里仔细看看 . 在极少数情况下它很有用,所以这就是为什么它值得知道怎么做 .
还想做一个......?
使用lazy-static
lazy-static箱子可以带走一些创造单身人士的苦差事(下图) . 这是一个全局可变向量:
如果你删除了
Mutex
那么你就有了一个没有任何可变性的全局单例 .一个特例:原子
如果您只需要跟踪整数值,则可以直接使用atomic:
手册,无依赖性实施
这很大程度上来自the Rust 1.0 implementation of stdin . 您还应该看一下io::Lazy的现代实现 . 我已经评论了每行的内容 .
打印出:
此代码使用Rust 1.23.0编译 .
Stdin
的实际实现使用一些不稳定的功能来尝试释放分配的内存,而这些代码没有 .真的,你可能想让
SingletonReader
实现Deref和DerefMut,这样你就不必戳入对象并自己锁定它 .所有这些工作都是lazy-static为您所做的 .