pub struct LocalKey<T: 'static> { /* private fields */ }
Expand description
拥有其内容的线程本地存储密钥。
该密钥使用可用于目标平台的最快速度的实现。它用 thread_local!
宏实例化,主要方法是 with
方法。
with
方法对包含的值产生一个引用,该值不能比当前线程长寿或转义给定的封闭包。
初始化与销毁
初始化是在线程中对 with
的第一次调用中动态执行的,并且当线程退出时,实现 Drop
的值将被销毁。一些注意事项适用,下面将进行说明。
LocalKey 的初始化不能递归地依赖于它自己,以这种方式使用 LocalKey
会使初始化在第一次调用 with
时无限递归。
Examples
use std::cell::RefCell;
use std::thread;
thread_local!(static FOO: RefCell<u32> = RefCell::new(1));
FOO.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 2;
});
// 每个线程以 1 的初始值开始
let t = thread::spawn(move|| {
FOO.with(|f| {
assert_eq!(*f.borrow(), 1);
*f.borrow_mut() = 3;
});
});
// 等待线程完成并在 panic 上退出
t.join().unwrap();
// 尽管有子线程,我们仍保留原始值 2
FOO.with(|f| {
assert_eq!(*f.borrow(), 2);
});
Run特定于平台的行为
请注意,我们尽了 “最大努力” 来确保运行线程本地存储中存储的类型的析构函数,但并非所有平台都能保证运行线程本地存储中所有类型的析构函数。
例如,有许多已知的警告未运行析构函数:
- 在 Unix 系统上,当使用基于 pthread 的 TLS 时,主线程退出时,不会为主线程上的 TLS 值运行析构函数。 请注意,应用程序也将在主线程退出后立即退出。
- 在所有平台上,TLS 都有可能在销毁期间重新初始化其他 TLS 插槽。 某些平台通过防止重新初始化已销毁的任何插槽来确保不会无限发生这种情况,但并非所有平台都具有此保护措施。 那些不受约束的平台通常具有综合限制,在此之后,将不再运行析构函数。
- 当进程在 Windows 系统上退出时,TLS 析构函数可能只在导致进程退出的线程上运行。这是因为其他线程可能会被强制终止。
线程本地析构函数中的同步
在 Windows 上,线程本地析构函数中的同步操作 (例如 JoinHandle::join
) 容易出现死锁,因此应该避免。
这是因为 loader lock 在运行析构函数时,仍被持有。每当线程启动、退出、加载或卸载 DLL 时,都会获取锁。
所以,只要线程本地析构函数正在运行,这些事件就会被阻止。
Implementations§
source§impl<T: 'static> LocalKey<T>
impl<T: 'static> LocalKey<T>
sourcepub fn with<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&T) -> R,
pub fn with<F, R>(&'static self, f: F) -> Rwhere F: FnOnce(&T) -> R,
获取对此 TLS 密钥中的值的引用。
如果此线程尚未引用此键,则将延迟地初始化该值。
Panics
如果该键当前正在运行其析构函数,则此函数将为 panic!()
; 如果先前已为此线程运行了析构函数,则它可能 panic。
1.26.0 · sourcepub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>where
F: FnOnce(&T) -> R,
pub fn try_with<F, R>(&'static self, f: F) -> Result<R, AccessError>where F: FnOnce(&T) -> R,
获取对此 TLS 密钥中的值的引用。
如果此线程尚未引用此键,则将延迟地初始化该值。
如果密钥已被销毁 (如果在析构函数中调用它可能会发生这种情况),此函数将返回 AccessError
。
Panics
如果未初始化密钥并且密钥的初始化 panics,则此函数仍将 panic!()
。
source§impl<T: 'static> LocalKey<Cell<T>>
impl<T: 'static> LocalKey<Cell<T>>
sourcepub fn set(&'static self, value: T)
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn set(&'static self, value: T)
local_key_cell_methods
#92122)设置或初始化包含的值。
与其他方法不同,这将不会运行本地线程的惰性初始化。 相反,如果它还没有初始化,它将直接用给定的值初始化。
Panics
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Examples
#![feature(local_key_cell_methods)]
use std::cell::Cell;
thread_local! {
static X: Cell<i32> = panic!("!");
}
// 在这里调用 X.get() 会导致 panic。
X.set(123); // 但是 X.set() 很好,因为它跳过了上面的初始化。
assert_eq!(X.get(), 123);
Runsourcepub fn get(&'static self) -> Twhere
T: Copy,
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn get(&'static self) -> Twhere T: Copy,
local_key_cell_methods
#92122)sourcepub fn take(&'static self) -> Twhere
T: Default,
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn take(&'static self) -> Twhere T: Default,
local_key_cell_methods
#92122)获取包含的值,在其位置保留 Default::default()
。
如果此线程尚未引用此键,则将延迟地初始化该值。
Panics
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Examples
#![feature(local_key_cell_methods)]
use std::cell::Cell;
thread_local! {
static X: Cell<Option<i32>> = Cell::new(Some(1));
}
assert_eq!(X.take(), Some(1));
assert_eq!(X.take(), None);
Runsource§impl<T: 'static> LocalKey<RefCell<T>>
impl<T: 'static> LocalKey<RefCell<T>>
sourcepub fn with_borrow<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&T) -> R,
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn with_borrow<F, R>(&'static self, f: F) -> Rwhere F: FnOnce(&T) -> R,
local_key_cell_methods
#92122)获取对所包含值的引用。
如果此线程尚未引用此键,则将延迟地初始化该值。
Panics
如果当前价值是可变借来的,就会出现 panic。
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Example
#![feature(local_key_cell_methods)]
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow(|v| assert!(v.is_empty()));
Runsourcepub fn with_borrow_mut<F, R>(&'static self, f: F) -> Rwhere
F: FnOnce(&mut T) -> R,
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn with_borrow_mut<F, R>(&'static self, f: F) -> Rwhere F: FnOnce(&mut T) -> R,
local_key_cell_methods
#92122)获取对所包含值的可变引用。
如果此线程尚未引用此键,则将延迟地初始化该值。
Panics
如果当前的值是借来的,就会出现 panic。
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Example
#![feature(local_key_cell_methods)]
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow_mut(|v| v.push(1));
X.with_borrow(|v| assert_eq!(*v, vec![1]));
Runsourcepub fn set(&'static self, value: T)
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn set(&'static self, value: T)
local_key_cell_methods
#92122)设置或初始化包含的值。
与其他方法不同,这将不会运行本地线程的惰性初始化。 相反,如果它还没有初始化,它将直接用给定的值初始化。
Panics
如果当前的值是借来的,就会出现 panic。
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Examples
#![feature(local_key_cell_methods)]
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = panic!("!");
}
// 在这里调用 X.with() 会导致 panic。
X.set(vec![1, 2, 3]); // 但是 X.set() 很好,因为它跳过了上面的初始化。
X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
Runsourcepub fn take(&'static self) -> Twhere
T: Default,
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn take(&'static self) -> Twhere T: Default,
local_key_cell_methods
#92122)获取包含的值,在其位置保留 Default::default()
。
如果此线程尚未引用此键,则将延迟地初始化该值。
Panics
如果当前的值是借来的,就会出现 panic。
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Examples
#![feature(local_key_cell_methods)]
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
X.with_borrow_mut(|v| v.push(1));
let a = X.take();
assert_eq!(a, vec![1]);
X.with_borrow(|v| assert!(v.is_empty()));
Runsourcepub fn replace(&'static self, value: T) -> T
🔬This is a nightly-only experimental API. (local_key_cell_methods
#92122)
pub fn replace(&'static self, value: T) -> T
local_key_cell_methods
#92122)替换包含的值,返回旧值。
Panics
如果当前的值是借来的,就会出现 panic。
如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。
Examples
#![feature(local_key_cell_methods)]
use std::cell::RefCell;
thread_local! {
static X: RefCell<Vec<i32>> = RefCell::new(Vec::new());
}
let prev = X.replace(vec![1, 2, 3]);
assert!(prev.is_empty());
X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
Run