use*_*932 5 multithreading rust
我想在new方法内部创建一个线程,并在结构被销毁后停止它:
use std::thread;
struct Foo {
handle: thread::JoinHandle<()>,
}
impl Foo {
pub fn new(name: &str) -> Foo {
let name = name.to_string();
Foo {
handle: thread::spawn(move || {
println!("hi {}", name);
}),
}
}
pub fn stop(&mut self) {
self.handle.join();
}
}
fn main() {
let mut foo = Foo::new("test");
foo.stop();
}
Run Code Online (Sandbox Code Playgroud)
这不编译,我不明白为什么:
error[E0507]: cannot move out of borrowed content
--> <anon>:15:9
|
15 | self.handle.join();
| ^^^^ cannot move out of borrowed content
Run Code Online (Sandbox Code Playgroud)
我该如何解决这个错误?
今后,我将实施Drop的Foo,并调用stop()从drop().
功能签名JoinHandle::join是:
fn join(self) -> Result<T>
Run Code Online (Sandbox Code Playgroud)
这意味着该方法self通过值(获取所有权/消费它)获取(接收者对象).但你只能向你借钱JoinHandle; 一个可变的,但仍然只是借入,而不是所有权.因此,您无法调用此方法,因为您无法将所有权从借用移到此join()方法中.
解决这个问题的一种简单方法是通过接受方法中self的值stop():
pub fn stop(self) {
self.handle.join();
}
Run Code Online (Sandbox Code Playgroud)
但是你会发现实施时这是不可能的Drop,因为drop()有签名fn drop(&mut self)!坏消息!但是你可以使用一个小技巧,如下所述.请注意,加入主题drop()可能不是一个好主意!阅读Matthieu M.的答案,了解更多相关信息!
如果你仍然认为,无论出于何种原因,你真的想加入一个帖子drop(),你可以存储JoinHandle一个Option<T>来保存它是否已经加入.如果你有一个,Some(T)你可以T使用该方法从中获得(按值!)Option::take().然后你可以写:
fn drop(&mut self) {
// `self.handle` has the type `Option<JoinHandle<()>>` here!
if let Some(handle) = self.handle.take() {
handle.join().expect("failed to join thread");
}
}
Run Code Online (Sandbox Code Playgroud)
不要。
看起来似乎违反直觉,但是在析构函数中加入线程(或进程)通常不是一个好主意。
请注意,要求加入并不会导致线程自行停止;而是会导致线程自动停止。它只是在等待线程停止,而线程可能不会。发生这种情况的原因有多种:
是的,这是一个僵局。一个隐含的僵局。
一个特别令人讨厌的情况是,如果您当前的线程出现紧急情况(发生了无法预料的事情)。放卷开始...并阻塞!并且该线程本应清除的所有资源永远都处于困境。
更好的设计是创建一个显式join方法,该方法使用self(按值)消耗。Result如果连接导致错误,它还允许您返回。
并且为了让您的用户记住忘记加入,请panic!在Drop实现中明确加入。
那是:
impl Foo {
fn join(mut self) -> std::thread::Result<()> {
match self.handle.take() {
Some(h) => h.join(),
None => Ok(()),
}
}
}
impl Drop for Foo {
fn drop(&mut self) {
if self.handle.is_some() {
panic!("You MUST call either join on `Foo` to clean it up.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
注意:我知道破坏程序的恐慌是有争议的,但是,如果进程处于未知状态并希望继续前进,则中止进程更为安全。
如果您确实在我的警告下仍然想朝自己的脚开枪,那就加入吧drop。