gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-29 08:33:34 27 4
gpt4 key购买 nike

创建和使用系统中只有一个实例化的结构的最佳方法是什么?是的,这是必要的,它是 OpenGL 子系统,制作多个副本并将其传递到各处会增加困惑,而不是减轻困惑。

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

最佳答案

非答案答案

通常避免使用全局状态。相反,尽早在某处构造对象(可能在 main 中),然后将对该对象的可变引用传递到需要它的地方。这通常会使您的代码更易于推理,并且不需要向后弯腰。

在决定是否需要全局可变变量之前,仔细看看镜子里的自己。它在极少数情况下有用,因此值得了解如何去做。

还想做一个...?

提示

在以下3种解决方案中:

  • 如果删除 Mutex那么你就有了一个没有任何可变性的全局单例
  • 您也可以使用 RwLock而不是 Mutex允许多个并发读者

使用惰性静态

lazy-static crate 可以消除手动创建单例的一些苦差事。这是一个全局可变向量:

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());
}

使用once_cell

once_cell crate 可以消除手动创建单例的一些苦差事。这是一个全局可变向量:

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());
}

使用std::sync::LazyLock

标准库在the process添加 once_cell 的功能,目前称为 LazyLock :

#![feature(once_cell)] // 1.67.0-nightly
use std::sync::{LazyLock, Mutex};

static ARRAY: LazyLock<Mutex<Vec<u8>>> = LazyLock::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());
}

一个特例:原子

如果你只需要跟踪一个整数值,你可以直接使用一个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 的想法相同,例如使用 MaybeUninit 来避免分配和不必要的间接访问。您还应该看看 io::Lazy 的现代实现.我已经对每一行的内容进行了内联评论。

use std::sync::{Mutex, Once};
use std::time::Duration;
use std::{mem::MaybeUninit, thread};

struct SingletonReader {
// Since we will be used in many threads, we need to protect
// concurrent access
inner: Mutex<u8>,
}

fn singleton() -> &'static SingletonReader {
// Create an uninitialized static
static mut SINGLETON: MaybeUninit<SingletonReader> = MaybeUninit::uninit();
static ONCE: Once = Once::new();

unsafe {
ONCE.call_once(|| {
// Make it
let singleton = SingletonReader {
inner: Mutex::new(0),
};
// Store it to the static var, i.e. initialize it
SINGLETON.write(singleton);
});

// Now we give out a shared reference to the data, which is safe to use
// concurrently.
SINGLETON.assume_init_ref()
}
}

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.55.0 编译。

所有这些工作都是 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/38362000/

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