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了当前函数所拥有的
这是错误的,因为:
它在这里所指的功能是(我相信)主要的。一旦 main 完成执行,线程将被杀死或进入 UB。
它所指的函数清楚地在所述线程上调用 .join() 。
以前的代码在任何方面都不安全吗?如果是这样,为什么?如果没有,我怎样才能让编译器理解这一点?
编辑:是的,我知道在这种情况下我可以只移动消息,我的问题是专门询问如何传递对它的引用(最好不必堆分配它,类似于此代码的执行方式:
std::thread([&message]() -> void {/* etc */});
Run Code Online (Sandbox Code Playgroud)
(只是为了澄清,我实际上试图做的是从两个线程访问线程安全的数据结构......不涉及使复制工作的问题的其他解决方案也会有所帮助)。
Edit2:这个被标记为重复的问题有 5 页长,因此我认为它本身就是无效的问题。
以前的代码在任何方面都是“不安全的”吗?如果是这样,为什么?
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();
}
Run Code Online (Sandbox Code Playgroud)
一个人可以告诉每个线程在main退出之前加入。但是打字员无法知道这一点。
- 它在这里所指的功能是(我相信)主要的。所以大概这些线程会在 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)
| 归档时间: |
|
| 查看次数: |
767 次 |
| 最近记录: |