2

我有以下代码:

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();
}

rustc给我编译错误:

闭包可能比当前函数寿命更长,但它借用message了当前函数所拥有的

这是错误的,因为:

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

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

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

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

std::thread([&message]() -> void {/* etc */});

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

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

4

1 回答 1

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();
}

人类可以告诉每个线程在main退出之前加入。但是类型检查器无法知道这一点。

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

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

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

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

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

let t1 = std::thread::spawn(|| {

let t1 = std::thread::spawn(move || {
于 2017-08-03T04:05:32.490 回答