pull/1522/merge
zrll 2 weeks ago committed by GitHub
commit cbc6831a3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -313,22 +313,27 @@ fn main() {
}
```
## 标准库中的 OnceCell
## 标准库中的 Once 和 Lazy
`Rust` 标准库中提供了实验性的 `lazy::OnceCell``lazy::SyncOnceCell` (在 `Rust`
1.70.0版本及以上的标准库中,替换为稳定的 `cell::OnceCell``sync::OnceLock` )两种
`Cell` ,前者用于单线程,后者用于多线程,它们用来存储堆上的信息,并且具有最
多只能赋值一次的特性。 如实现一个多线程的日志组件 `Logger`
在 Rust 1.70.0 版本之前,标准库通过实验性 API 提供 `lazy::OnceCell``lazy::SyncOnceCell` 来实现单次初始化容器,这时,懒初始化主要依靠 `lazy_static`
随着 `cell::OnceCell``sync::OnceLock` 在 1.70.0 中稳定和 `LazyCell``LazyLock` 在 1.80.0 中稳定,只使用标准库实现懒加载成为可能。
这4个容器可以这样区分
```rust
// 低于Rust 1.70版本中, OnceCell 和 SyncOnceCell 的API为实验性的
// 需启用特性 `#![feature(once_cell)]`
#![feature(once_cell)]
use std::{lazy::SyncOnceCell, thread};
| | 单线程 | 多线程 |
|------|----------|----------|
| Once | OnceCell | OnceLock |
| Lazy | LazyCell | LazyLock |
其中,`Lazy` 会自动按需加载内容,让代码更简洁,更人性化,而 `Once` 则可以手动指定初始化的时机或使用不同的方法初始化,更强大。
`Cell` 的实现更简单,效率也更高,但是他并不保证线程安全,而 `Lock` 通过内部同步机制实现了线程安全。
#### Once 使用方法
// Rust 1.70版本以上,
// use std::{sync::OnceLock, thread};
下面,我们先看一下 `Once` 的使用方法:
```rust
use std::{sync::OnceLock, thread};
fn main() {
// 子线程中调用
@ -350,11 +355,7 @@ fn main() {
#[derive(Debug)]
struct Logger;
// 低于Rust 1.70版本
static LOGGER: SyncOnceCell<Logger> = SyncOnceCell::new();
// Rust 1.70版本以上
// static LOGGER: OnceLock<Logger> = OnceLock::new();
static LOGGER: OnceLock<Logger> = OnceLock::new();
impl Logger {
fn global() -> &'static Logger {
@ -371,7 +372,8 @@ impl Logger {
}
```
以上代码我们声明了一个 `global()` 关联函数,并在其内部调用 `get_or_init` 进行初始化 `Logger`,之后在不同线程上多次调用 `Logger::global()` 获取其实例:
以上代码我们声明了一个 `global()` 关联函数,并在其内部调用 `get_or_init` 进行初始化 `Logger`,之后在不同线程上多次调用 `Logger::global()` 获取其实例。
由于需要在多个线程中使用,所以我们使用了 `OnceLock`。以下是输出:
```console
Logger is being created...
@ -382,7 +384,49 @@ thread message
可以看到,`Logger is being created...` 在多个线程中使用也只被打印了一次。
特别注意,目前 `OnceCell``SyncOnceCell` API 暂未稳定,需启用特性 `#![feature(once_cell)]`
#### Lazy 使用方法
上述例子如果使用 `LazyLock` 来实现,则可以变得更加简洁:
```rust
use std::{sync::LazyLock, thread};
fn main() {
// 子线程中调用
let handle = thread::spawn(|| {
let logger = &LOGGER;
logger.log("thread message".to_string());
});
// 主线程调用
let logger = &LOGGER;
logger.log("some message".to_string());
let logger2 = &LOGGER;
logger2.log("other message".to_string());
handle.join().unwrap();
}
#[derive(Debug)]
struct Logger;
static LOGGER: LazyLock<Logger> = LazyLock::new(Logger::new);
impl Logger {
fn new() -> Logger {
println!("Logger is being created...");
Logger
}
fn log(&self, message: String) {
println!("{}", message)
}
}
```
以上代码中,我们使用 `LazyLock::new` 方法直接对全局变量 `LOGGER` 进行赋值,并传入一个初始化函数。
使用的时候,可以直接使用对全局变量的引用。在获取引用的时候, `LazyLock` 会自动检查初始化的状态并进行初始化。
## 总结

Loading…
Cancel
Save