gpt4 book ai didi

rust - 如何创建全局的可变单例?

转载 作者:行者123 更新时间:2023-12-03 07:39:13 25 4
gpt4 key购买 nike

在系统中只有一个实例的情况下,创建和使用结构的最佳方法是什么?是的,这是必需的,它是OpenGL子系统,将其制作多个副本并将其传递到任何地方都会增加困惑,而不是缓解它。

单例需要尽可能高效。似乎不可能在静态区域上存储任意对象,因为它包含带有析构函数的Vec。第二种选择是在静态区域上存储一个(不安全的)指针,该指针指向分配给堆的单例。在保持语法简洁的同时,最方便,最安全的方法是什么。

最佳答案

非答案
通常避免使用全局状态。取而代之的是,尽早在某个地方构造该对象(也许在main中),然后将对该对象的可变引用传递到需要它的位置。通常,这将使您的代码更易于推理,不需要向后弯曲。
在决定要使用全局可变变量之前,请仔细照镜子自己。在极少数情况下,它很有用,这就是为什么值得知道如何做的原因。
还想做一个...吗?
使用延迟静态
lazy-static条板箱可以消除手动创建单例的繁琐工作。这是一个全局可变 vector :

use lazy_static::lazy_static; // 1.4.0
use std::sync::Mutex;

lazy_static! {
static ref ARRAY: Mutex<Vec<u8>> = Mutex::new(vec![]);
}

fn do_a_call() {
ARRAY.lock().unwrap().push(1);
}

fn main() {
do_a_call();
do_a_call();
do_a_call();

println!("called {}", ARRAY.lock().unwrap().len());
}
如果删除 Mutex ,那么您将拥有一个全局单例而没有任何可变性。
您也可以使用 RwLock 而不是 Mutex来允许多个并发阅读器。
使用one_cell
once_cell条板箱可以消除手动创建单例的繁琐工作。这是一个全局可变 vector :
use once_cell::sync::Lazy; // 1.3.1
use std::sync::Mutex;

static ARRAY: Lazy<Mutex<Vec<u8>>> = Lazy::new(|| Mutex::new(vec![]));

fn do_a_call() {
ARRAY.lock().unwrap().push(1);
}

fn main() {
do_a_call();
do_a_call();
do_a_call();

println!("called {}", ARRAY.lock().unwrap().len());
}
如果删除 Mutex ,那么您将拥有一个全局单例而没有任何可变性。
您也可以使用 RwLock 而不是 Mutex来允许多个并发阅读器。
使用 std::sync::SyncLazy标准库位于 the process中,用于添加 once_cell的功能,当前称为 SyncLazy :
#![feature(once_cell)] // 1.48.0-nightly (2020-08-28 d006f5734f49625c34d6)
use std::{lazy::SyncLazy, sync::Mutex};

static ARRAY: SyncLazy<Mutex<Vec<u8>>> = SyncLazy::new(|| Mutex::new(vec![]));

fn do_a_call() {
ARRAY.lock().unwrap().push(1);
}

fn main() {
do_a_call();
do_a_call();
do_a_call();

println!("called {}", ARRAY.lock().unwrap().len());
}

如果删除 Mutex ,那么您将拥有一个全局单例而没有任何可变性。
您也可以使用 RwLock 而不是 Mutex来允许多个并发阅读器。
特例:原子
如果只需要跟踪整数值,则可以直接使用 atomic:
use std::sync::atomic::{AtomicUsize, Ordering};

static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);

fn do_a_call() {
CALL_COUNT.fetch_add(1, Ordering::SeqCst);
}

fn main() {
do_a_call();
do_a_call();
do_a_call();

println!("called {}", CALL_COUNT.load(Ordering::SeqCst));
}
手动,无依赖实现
the Rust 1.0 implementation of stdin 大大削弱了它,并对现代Rust进行了一些调整。您还应该看看 io::Lazy 的现代实现。我已对每一行的内容进行了内联注释。
use std::sync::{Arc, Mutex, Once};
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::new();

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
该代码使用Rust 1.42.0进行编译。 Stdin的实际实现使用一些不稳定的功能来尝试释放已分配的内存,而此代码不会。
确实,您可能希望使 SingletonReader实现 Deref DerefMut ,因此您不必戳入对象并自己锁定它。
所有这些工作都是lazy-static或Once_cell为您完成的。
“全局”的意思
请注意,您仍然可以使用常规Rust范围和模块级别的隐私来控制对 staticlazy_static变量的访问。这意味着您可以在模块中甚至在函数内部声明它,并且无法在该模块/函数外部访问它。这对于控制访问非常有用:
use lazy_static::lazy_static; // 1.2.0

fn only_here() {
lazy_static! {
static ref NAME: String = String::from("hello, world!");
}

println!("{}", &*NAME);
}

fn not_here() {
println!("{}", &*NAME);
}
error[E0425]: cannot find value `NAME` in this scope
--> src/lib.rs:12:22
|
12 | println!("{}", &*NAME);
| ^^^^ not found in this scope
但是,该变量仍然是全局变量,因为在整个程序中存在一个实例。

关于rust - 如何创建全局的可变单例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65292878/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com