Struct std::thread::LocalKey

1.0.0 · source ·
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

特定于平台的行为

请注意,我们尽了 “最大努力” 来确保运行线程本地存储中存储的类型的析构函数,但并非所有平台都能保证运行线程本地存储中所有类型的析构函数。

例如,有许多已知的警告未运行析构函数:

  1. 在 Unix 系统上,当使用基于 pthread 的 TLS 时,主线程退出时,不会为主线程上的 TLS 值运行析构函数。 请注意,应用程序也将在主线程退出后立即退出。
  2. 在所有平台上,TLS 都有可能在销毁期间重新初始化其他 TLS 插槽。 某些平台通过防止重新初始化已销毁的任何插槽来确保不会无限发生这种情况,但并非所有平台都具有此保护措施。 那些不受约束的平台通常具有综合限制,在此之后,将不再运行析构函数。
  3. 当进程在 Windows 系统上退出时,TLS 析构函数可能只在导致进程退出的线程上运行。这是因为其他线程可能会被强制终止。

线程本地析构函数中的同步

在 Windows 上,线程本地析构函数中的同步操作 (例如 JoinHandle::join) 容易出现死锁,因此应该避免。 这是因为 loader lock 在运行析构函数时,仍被持有。每当线程启动、退出、加载或卸载 DLL 时,都会获取锁。 所以,只要线程本地析构函数正在运行,这些事件就会被阻止。

Implementations§

source§

impl<T: 'static> LocalKey<T>

source

pub fn with<F, R>(&'static self, f: F) -> Rwhere F: FnOnce(&T) -> R,

获取对此 TLS 密钥中的值的引用。

如果此线程尚未引用此键,则将延迟地初始化该值。

Panics

如果该键当前正在运行其析构函数,则此函数将为 panic!(); 如果先前已为此线程运行了析构函数,则它可能 panic。

1.26.0 · source

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>>

source

pub fn set(&'static self, value: T)

🔬This is a nightly-only experimental API. (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);
Run
source

pub fn get(&'static self) -> Twhere T: Copy,

🔬This is a nightly-only experimental API. (local_key_cell_methods #92122)

返回所包含值的副本。

如果此线程尚未引用此键,则将延迟地初始化该值。

Panics

如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。

Examples
#![feature(local_key_cell_methods)]
use std::cell::Cell;

thread_local! {
    static X: Cell<i32> = Cell::new(1);
}

assert_eq!(X.get(), 1);
Run
source

pub fn take(&'static self) -> Twhere T: Default,

🔬This is a nightly-only experimental API. (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);
Run
source

pub fn replace(&'static self, value: T) -> T

🔬This is a nightly-only experimental API. (local_key_cell_methods #92122)

替换包含的值,返回旧值。

如果此线程尚未引用此键,则将延迟地初始化该值。

Panics

如果密钥当前正在运行其析构函数,则发生 panic,如果析构函数先前已为此线程运行,则它可能panic。

Examples
#![feature(local_key_cell_methods)]
use std::cell::Cell;

thread_local! {
    static X: Cell<i32> = Cell::new(1);
}

assert_eq!(X.replace(2), 1);
assert_eq!(X.replace(3), 2);
Run
source§

impl<T: 'static> LocalKey<RefCell<T>>

source

pub 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)

获取对所包含值的引用。

如果此线程尚未引用此键,则将延迟地初始化该值。

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()));
Run
source

pub 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)

获取对所包含值的可变引用。

如果此线程尚未引用此键,则将延迟地初始化该值。

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]));
Run
source

pub fn set(&'static self, value: T)

🔬This is a nightly-only experimental API. (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]));
Run
source

pub fn take(&'static self) -> Twhere T: Default,

🔬This is a nightly-only experimental API. (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()));
Run
source

pub fn replace(&'static self, value: T) -> T

🔬This is a nightly-only experimental API. (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

Trait Implementations§

1.16.0 · source§

impl<T: 'static> Debug for LocalKey<T>

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

使用给定的格式化程序格式化该值。 Read more

Auto Trait Implementations§

§

impl<T> RefUnwindSafe for LocalKey<T>

§

impl<T> Send for LocalKey<T>

§

impl<T> Sync for LocalKey<T>

§

impl<T> Unpin for LocalKey<T>

§

impl<T> UnwindSafe for LocalKey<T>

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

获取 selfTypeIdRead more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

从拥有的值中一成不变地借用。 Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

从拥有的值中借用。 Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

返回未更改的参数。

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

调用 U::from(self)

也就是说,这种转换是 From<T> for U 实现选择执行的任何操作。

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

发生转换错误时返回的类型。
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

执行转换。
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

发生转换错误时返回的类型。
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

执行转换。