在一个采用“& mut self”(如drop)的方法中加入一个线程会导致“无法移出借来的内容”

时间:2016-12-26 12:50:42

标签: multithreading rust

我想在 <Scene key="dashBoard" component={DashBoard} type="reset" renderTitle={() => <View style={Styles.renderTitleStyle}> <Image source={Images.logo}/> </View> } onRightButton={() => console.log('print')} renderRightButton={() => <TouchableOpacity onPress={() => this.logOut()}> <Icon name="md-power" color="#FFFFFF" style={{fontSize: 25, top: 0}}/> </TouchableOpacity> } /> 方法内创建一个线程,并在结构被销毁后停止它:

logOut(){
 console.log('logout');
}

这不能编译,我无法理解为什么:

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(); } 实施error[E0507]: cannot move out of borrowed content --> <anon>:15:9 | 15 | self.handle.join(); | ^^^^ cannot move out of borrowed content ,并会从Drop致电Foo

4 个答案:

答案 0 :(得分:8)

JoinHandle::join的功能签名是:

fn join(self) -> Result<T>

这意味着该方法按值获取self(接收者对象)(获取所有权/消费它)。但是你只能借用JoinHandle;一个可变的,但仍然只是借用,所有权。因此,您无法调用此方法,因为您无法将所有权从借用中转移到此join()方法中。

解决这个问题的一种简单方法是,通过self方法中的值接受stop()

pub fn stop(self) {
    self.handle.join();
}

但是您会注意到在实施Drop时无法做到这一点,因为drop()具有签名fn drop(&mut self)!坏消息!但是你可以使用一个小技巧,如下所述。请注意,加入drop()中的主题可能不是一个好主意!阅读Matthieu M.'s answer了解更多相关信息!

如果你仍然认为,无论出于何种原因,你真的想加入drop()中的一个帖子,你可以将JoinHandle存储在Option<T>中以保存它是否已经存在加入。如果您有Some(T),则可以使用方法Option::take()从中获取T(按值!)。然后你可以写:

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");
    }
}

答案 1 :(得分:3)

<强>不

这看似违反直觉,但在析构函数中加入一个线程(或进程)通常是一个坏主意

请注意,要求加入不会导致线程自行停止;它只是等待让线程停止,而线程可能没有。导致这种情况发生的原因有很多:

  • 句柄与其控制的句柄位于同一个帖子上,
  • 要停止等待当前线程应该发送信号的通道的线程,
  • ...

是的,这是一个僵局。 隐式死锁。

一个特别令人讨厌的情况是,如果你当前的线程发生恐慌(发生了无法预料的事情)。放松开始...并阻止!而这个线程应该清理的所有资源永远都处于不确定状态。

更好的设计是创建一个明确的join方法,它消耗self(按值)。它还允许您返回Result,以防加入导致错误。

为了让您的用户忘记明确加入panic!Drop如果他们忘记了。

那是:

impl Foo {
    fn join(self) -> std::thread::Result<()> {
        self.handle.join()
    }
}

impl Drop for Foo {
    fn drop(&mut self) {
        panic!("You MUST call either join on `Foo` to clean it up.");
    }
}

注意:我知道在析构函数中恐慌是有争议的,但是当一个进程处于未知状态并且希望最好时,中止进程会更加安全。

如果你真的,尽管我发出了警告,想要用脚射击自己,使用Option及其take方法。

答案 2 :(得分:2)

join签名中的问题:

fn join(self) -> Result<T>

所以为了修复你的代码,你需要这样的东西:

pub fn stop(self) {
    self.handle.join();
}

答案 3 :(得分:1)

如果您不介意不安全的代码,则可以执行以下操作(请查看Matthieus answer为什么此 可能不是一个好主意)。

struct Foo {
    handle: ManuallyDrop<thread::JoinHandle<()>>,
}

impl Drop for Foo {
    fn drop(&mut self) {
        unsafe {
            let _ = ManuallyDrop::take(&mut self.handle).join();
        }
    }
}