如何告诉 Rust 一个线程的寿命不会比它的调用者长?

Geo*_*rge 1 lambda multithreading rust borrow-checker borrowing

我有以下代码:

fn main() {
    let message = "Can't shoot yourself in the foot if you ain't got no gun";
    let t1 = std::thread::spawn(|| {
        println!("{}", message);
    });
    t1.join();
}
Run Code Online (Sandbox Code Playgroud)

rustc 给我编译错误:

闭包可能比当前函数存活时间更长,但它借用message了当前函数所拥有的

这是错误的,因为:

  1. 它在这里所指的功能是(我相信)主要的。一旦 main 完成执行,线程将被杀死或进入 UB。

  2. 它所指的函数清楚地在所述线程上调用 .join() 。

以前的代码在任何方面都不安全吗?如果是这样,为什么?如果没有,我怎样才能让编译器理解这一点?

编辑:是的,我知道在这种情况下我可以只移动消息,我的问题是专门询问如何传递对它的引用(最好不必堆分配它,类似于此代码的执行方式:

std::thread([&message]() -> void {/* etc */});
Run Code Online (Sandbox Code Playgroud)

(只是为了澄清,我实际上试图做的是从两个线程访问线程安全的数据结构......不涉及使复制工作的问题的其他解决方案也会有所帮助)。

Edit2:这个被标记为重复的问题有 5 页长,因此我认为它本身就是无效的问题。

log*_*yth 5

以前的代码在任何方面都是“不安全的”吗?如果是这样,为什么?

Rust 的类型检查和借用检查系统的目标是禁止不安全的程序,但这并不意味着所有编译失败的程序都是不安全的。在这种特定情况下,您的代码并非不安全,但它不满足您正在使用的函数的类型约束。

  1. 它所指的函数清楚地在所述线程上调用 .join() 。

但是从类型检查器的角度来看,没有什么需要调用.join. 类型检查系统(就其本身而言)不能强制对给定对象调用或未调用函数。你可以很容易地想象一个例子

let message = "Can't shoot yourself in the foot if you ain't got no gun";
let mut handles = vec![];
for i in 0..3 {
    let t1 = std::thread::spawn(|| {
        println!("{} {}", message, i);
    });
    handles.push(t1);
}
for t1 in handles {
    t1.join();
}
Run Code Online (Sandbox Code Playgroud)

一个人可以告诉每个线程在main退出之前加入。但是打字员无法知道这一点。

  1. 它在这里所指的功能是(我相信)主要的。所以大概这些线程会在 main 存在时被杀死(并且它们在 main 存在之后运行是 ub)。

从跳棋的角度来看,main只是另一个功能。没有特别的知识知道这个特定的函数可以有额外的行为。如果这是任何其他函数,线程将不会被自动终止。对此进行扩展,即使main不能保证子线程会立即被杀死。如果子线程被杀死需要 5 毫秒,那仍然是 5 毫秒,子线程可以访问超出范围的变量的内容。

要使用此特定代码段(按原样)获得您正在寻找的行为,闭包的生命周期必须与t1对象的生命周期相关联,以便在句柄完成后保证永远不会使用闭包被清理干净。虽然这当然是一种选择,但在一般情况下它的灵活性要低得多。因为它将在类型级别强制执行,所以无法选择退出此行为。

您可以考虑使用crossbeam,特别是crossbeam::scope's.spawn,它在标准库没有的情况下强制执行此生命周期要求,这意味着线程必须scope完成之前停止执行。

在您的特定情况下,只要您将所有权转移message到子线程而不是从main函数中借用它,您的代码就可以正常工作,因为无论是否调用.join. 如果您更改,您的代码工作正常

let t1 = std::thread::spawn(|| {
Run Code Online (Sandbox Code Playgroud)

let t1 = std::thread::spawn(move || {
Run Code Online (Sandbox Code Playgroud)

  • @George:请注意,Rust 存在的真正原因是让编译器消除 UB。这就是 Graydon Hoare 所追求的,也是 Mozilla 决定资助一个全职团队来研究它的原因。所以,是的,rustc 会尽最大努力防止 UB;这就是它的创建目的;) (3认同)