我有以下代码:
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
,它由当前函数拥有
这是错误的:
这里所指的功能是(我相信)主要的。一旦主要执行完毕,线程将被杀死或进入UB。
它所指的功能在所述线程上明确调用.join()。
以前的代码是否以任何方式不安全?如果是这样,为什么?如果没有,我怎样才能让编译器理解它?
编辑:是的我知道我可以在这种情况下移动消息,我的问题是具体询问我如何传递对它的引用(最好不必堆分配它,类似于这段代码的方式:
std::thread([&message]() -> void {/* etc */});
(只是为了澄清一下,我实际上要做的是从两个线程访问一个线程安全的数据结构......这个问题的其他解决方案也不会让复制工作也有帮助。)
Edit2:这个被标记为副本的问题长达5页,因此我认为它本身就是无效问题。
答案 0 :(得分:4)
以前的代码是否“不安全”?如果是这样,为什么?
Rust的类型检查和借用检查系统的目标是禁止不安全的程序,但这并不意味着所有未能编译的程序都是不安全的。在这种特定情况下,您的代码并不安全,但它不满足您正在使用的函数的类型约束。
- 它所指的功能明确地在所述线程上调用.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
退出之前判断每个线程是否已连接。但是一个类型检查员无法知道这一点。
- 这里所指的功能是(我相信)主要。因此,当主要存在时,大概存在这些线程将被杀死(并且它们在main存在之后运行是ub)。
醇>
从检查员的角度来看,main
只是另一个功能。没有特别的知识,这个特定的功能可以有额外的行为。如果这是任何其他函数,则该线程不会被自动杀死。即便对main
进行扩展,也无法保证子线程将立即被杀死。如果子线程被杀死需要5ms,那么子线程可以访问超出范围的变量的内容仍然是5ms。
要使用此特定代码段(原样)获得您正在寻找的行为,闭包的生命周期必须与t1
对象的生命周期相关联,以便保证闭包在清理手柄后永远不要使用。虽然这肯定是 选项,但在一般情况下灵活性明显较低。因为它会在类型级别强制执行,所以无法选择退出此行为。
你可以考虑使用crossbeam
,特别是crossbeam::scope
's .spawn
,它强制执行标准库所没有的生命周期要求,这意味着一个线程必须在{{1}之前停止执行已完成。
在您的特定情况下,只要您将scope
的所有权转移到子线程而不是从message
函数借用它,您的代码就可以正常工作,因为不存在不安全代码的风险或者没有致电main
。如果您更改
.join
到
let t1 = std::thread::spawn(|| {