#global #thread #tls #thread-local-storage #local-storage

threadstack

更舒适且更灵活的线程局部存储形式

5 个版本 (3 个破坏性更新)

0.4.1 2021 年 1 月 1 日
0.4.0 2021 年 1 月 1 日
0.3.0 2020 年 12 月 8 日
0.2.0 2020 年 12 月 7 日
0.1.0 2020 年 12 月 6 日

#560 in 并发

MIT 许可证

21KB
207

更舒适且更灵活的线程局部存储形式。

Docs Status license crates.io badge

灵感来自 Racket 的 参数功能

总体思路如下。许多应用程序都有“上下文”变量,这些变量几乎在应用程序的每个模块中都需要。通过程序中的每个函数传递这些值非常繁琐。明显的诱惑是使用全局变量,但全局变量有很多已知的缺点

  • 它们缺乏线程安全性。

  • 它们在你的应用程序的模块之间创建了一个隐藏的副作用通道,可以创建“鬼魅般的远程作用”。

  • 因为只有一个全局变量的实例,所以程序中的模块会争夺它们想要的值。

Threadstacks 是一个折中方案。本质上,你不再有一个全局变量,而是保持一个线程局部值栈。你只能引用栈顶的值,借用检查器将保证在值弹出之前你的引用消失。你可以在栈上推送新的值,但它们会在包含你的推送的词法作用域结束时自动过期。除非你刻意使用具有内部可变性的类型,如 CellRefCell,否则线程栈上的值是不可变的,所以通常通过在栈上推送而不是覆盖现有值来修改值的代码将这样做。

这相当于一个你可以临时覆盖的全局变量。以前会引用全局变量的函数现在会引用栈顶,通过在调用这些函数之前在栈上推送值,你可以影响它们的行为。然而,当你调用者的调用这些函数时,你不能影响它们的行为,因为当你调用者返回控制权时,包含你的推送的词法作用域已经结束,你推送的值已经自动从栈中弹出。这限制了不同模块相互干扰的程度。

因为提供的 let_ref_thread_stack_value! 创建了与当前栈帧绑定的特殊生命周期的引用,所以没有必要将使用线程栈值的所有代码都包裹在类似于 my_local_key.with(|data| {...}) 的调用中,就像你使用标准 thread_local! TLS 实现时必须做的那样。

示例

use threadstack::*;

declare_thread_stacks!(
    FOO: String = String::from("hello world");
);

let_ref_thread_stack_value!(my_reference, FOO);
assert!(my_reference == "hello world");

{
    push_thread_stack_value!("hello universe".into(), FOO);
    let_ref_thread_stack_value!(my_other_reference, FOO);
    assert!(my_other_reference == "hello universe");
}

assert!(my_reference == "hello world");
push_thread_stack_value!("hello galaxy".into(), FOO);
assert!(my_reference == "hello world"); // still is reference to old value!
let_ref_thread_stack_value!(my_reference, FOO); // shadows the old reference
assert!(my_reference == "hello galaxy");

依赖关系

~41KB