如果创建可变变量和可变引用并在单独的线程中更改其中之一,Rust 会发生什么?
What will happen in Rust if create mutable variable and mutable reference and change one of them in separate thread?
我是 Rust 的新手,刚刚开始了解它的所有权系统(本书的第 4 章)。编译器会处理值和引用可能发生的许多错误,但让我们想象以下情况:
让我们假设有这样的代码,但是 change_string
打开一个新线程并在那里执行突变。
fn main() {
let mut s = String::from("Hello");
// perform in thread, so flow will not be synchronous
change_string(&mut s);
s.push_str("..........");
println!("{}", s);
}
fn change_string(some_string: &mut String) {
some_string.push_str(", World");
}
目前我收到 Hello, World..........
,但是如果添加 , World
将在单独的线程中,会发生什么情况?
Rust 不会让你做不安全的事情(不使用 unsafe
关键字),包括与线程相关的任何事情。为什么不试试看它是如何编译失败的呢?
fn change_string(some_string: &mut String) {
std::thread::spawn(move || {
some_string.push_str(", World");
});
}
它产生这个错误:
error[E0621]: explicit lifetime required in the type of `some_string`
--> src/main.rs:14:5
|
13 | fn change_string_1(some_string: &mut String) {
| ----------- help: add explicit lifetime `'static` to the type of `some_string`: `&'static mut std::string::String`
14 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^ lifetime `'static` required
这意味着您不能将具有非静态生命周期的引用传递给线程,因为该线程的生命周期可能比此类引用长,这样是不安全的。
您可能在考虑数据竞争问题,但实际上这不是问题所在。真正的问题是您的 String
可能与线程一样长。实际上,如果你使用 crossbeam
crate 中的作用域线程,它就可以正常工作:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|_| {
some_string.push_str(", World");
});
}).unwrap();
}
现在它可以工作了,因为 crossbeam::scope()
调用不会 return 直到内部生成的所有线程都完成。因此,你的 String
的生命周期总是比线程长,一切正常。
但是数据竞争呢?有 none,因为 Rust 中的 &mut
引用是 唯一的 。这意味着你不能让它们中的两个指向同一个对象,所以你从哪个线程改变你的对象并不重要(只要你的对象实现 Sync
但大多数都实现),你正在做只有一个,没有种族。
如果您尝试创建两个线程来修改对象,即使使用 crossbeam
:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|_| {
some_string.push_str(", World");
});
s.spawn(|_| {
some_string.push_str(", World");
});
}).unwrap();
}
您收到此错误消息:
error[E0524]: two closures require unique access to `some_string` at the same time
请记住,&mut
表示除了变异能力之外的唯一访问权限。
如果您尝试将引用移动到闭包中而不是借用它:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(move |_| {
some_string.push_str(", World");
});
s.spawn(move |_| {
some_string.push_str(", World");
});
}).unwrap();
}
你在第二个闭包中得到这个:
error[E0382]: use of moved value: `some_string`
因为引用已经移到第一个,无法再次使用。
当然,您可以从线程中生成一个线程,这将起作用:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|s| {
some_string.push_str(", World");
s.spawn(|_| {
some_string.push_str(", World");
});
});
}).unwrap();
}
但请注意它仍然完全没有竞争,因为一旦创建了第二个线程,第一个线程就失去了对唯一可变引用的访问权限。
我是 Rust 的新手,刚刚开始了解它的所有权系统(本书的第 4 章)。编译器会处理值和引用可能发生的许多错误,但让我们想象以下情况:
让我们假设有这样的代码,但是 change_string
打开一个新线程并在那里执行突变。
fn main() {
let mut s = String::from("Hello");
// perform in thread, so flow will not be synchronous
change_string(&mut s);
s.push_str("..........");
println!("{}", s);
}
fn change_string(some_string: &mut String) {
some_string.push_str(", World");
}
目前我收到 Hello, World..........
,但是如果添加 , World
将在单独的线程中,会发生什么情况?
Rust 不会让你做不安全的事情(不使用 unsafe
关键字),包括与线程相关的任何事情。为什么不试试看它是如何编译失败的呢?
fn change_string(some_string: &mut String) {
std::thread::spawn(move || {
some_string.push_str(", World");
});
}
它产生这个错误:
error[E0621]: explicit lifetime required in the type of `some_string`
--> src/main.rs:14:5
|
13 | fn change_string_1(some_string: &mut String) {
| ----------- help: add explicit lifetime `'static` to the type of `some_string`: `&'static mut std::string::String`
14 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^ lifetime `'static` required
这意味着您不能将具有非静态生命周期的引用传递给线程,因为该线程的生命周期可能比此类引用长,这样是不安全的。
您可能在考虑数据竞争问题,但实际上这不是问题所在。真正的问题是您的 String
可能与线程一样长。实际上,如果你使用 crossbeam
crate 中的作用域线程,它就可以正常工作:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|_| {
some_string.push_str(", World");
});
}).unwrap();
}
现在它可以工作了,因为 crossbeam::scope()
调用不会 return 直到内部生成的所有线程都完成。因此,你的 String
的生命周期总是比线程长,一切正常。
但是数据竞争呢?有 none,因为 Rust 中的 &mut
引用是 唯一的 。这意味着你不能让它们中的两个指向同一个对象,所以你从哪个线程改变你的对象并不重要(只要你的对象实现 Sync
但大多数都实现),你正在做只有一个,没有种族。
如果您尝试创建两个线程来修改对象,即使使用 crossbeam
:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|_| {
some_string.push_str(", World");
});
s.spawn(|_| {
some_string.push_str(", World");
});
}).unwrap();
}
您收到此错误消息:
error[E0524]: two closures require unique access to `some_string` at the same time
请记住,&mut
表示除了变异能力之外的唯一访问权限。
如果您尝试将引用移动到闭包中而不是借用它:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(move |_| {
some_string.push_str(", World");
});
s.spawn(move |_| {
some_string.push_str(", World");
});
}).unwrap();
}
你在第二个闭包中得到这个:
error[E0382]: use of moved value: `some_string`
因为引用已经移到第一个,无法再次使用。
当然,您可以从线程中生成一个线程,这将起作用:
fn change_string(some_string: &mut String) {
crossbeam::scope(|s| {
s.spawn(|s| {
some_string.push_str(", World");
s.spawn(|_| {
some_string.push_str(", World");
});
});
}).unwrap();
}
但请注意它仍然完全没有竞争,因为一旦创建了第二个线程,第一个线程就失去了对唯一可变引用的访问权限。