如何告诉Rust一个线程的活动时间不比它的调用者长?

时间:2017-08-03 01:14:30

标签: multithreading lambda 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();
}

rustc给出了编译错误:

  

闭包可能比当前函数更长,但它借用message,它由当前函数拥有

这是错误的:

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

  2. 它所指的功能在所述线程上明确调用.join()。

  3. 以前的代码是否以任何方式不安全?如果是这样,为什么?如果没有,我怎样才能让编译器理解它?

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

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

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

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

1 个答案:

答案 0 :(得分:4)

  

以前的代码是否“不安全”?如果是这样,为什么?

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

  
      
  1. 它所指的功能明确地在所述线程上调用.join()。
  2.   

但是从类型检查器的角度来看,要求调用.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存在之后运行是ub)。
  2.   

从检查员的角度来看,main只是另一个功能。没有特别的知识,这个特定的功能可以有额外的行为。如果这是任何其他函数,则该线程不会被自动杀死。即便对main进行扩展,也无法保证子线程将立即被杀死。如果子线程被杀死需要5ms,那么子线程可以访问超出范围的变量的内容仍然是5ms。

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

你可以考虑使用crossbeam,特别是crossbeam::scope's .spawn,它强制执行标准库所没有的生命周期要求,这意味着一个线程必须在{{1}之前停止执行已完成。

在您的特定情况下,只要您将scope的所有权转移到子线程而不是从message函数借用它,您的代码就可以正常工作,因为不存在不安全代码的风险或者没有致电main。如果您更改

,您的代码可以正常工作
.join

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