设计帮助:在结构中进行线程化

时间:2017-02-04 18:14:35

标签: multithreading rust

我是Rust的新手。作为一个学习练习,我正在尝试编写一个我曾用C ++编写的简单计时器结构。接口和实现看起来像这样:

pub struct Timer {
    handle: Option<std::thread::JoinHandle<()>>,
    alive: bool,
}

impl Timer {
    pub fn new() {
        Timer {
            handle: None,
            alive: false,
        }
    }

    pub fn start(&'static mut self) {
        // Oops! How do I do this?
        self.handle = Some(std::thread::spawn(move || {
            self.alive = true;
            self.loop()
        }));
    }

    pub fn stop(&mut self) {
        self.alive = false;
        self.handle.unwrap().join()
    }

    pub fn loop(&self) {
        // while alive
    }
}

我理解为什么这是一个错误,因为use of moved value: self函数中的start,但我想知道我应该如何设计我的结构这样的东西这样会起作用。在我能想到的每一个场景中,我总会有双重借款的情况。

我有一种预感,我需要了解更多关于室内可变性的内容,但我认为在再下一个兔子洞之前我会要求设计指导。

1 个答案:

答案 0 :(得分:2)

我认为你非常接近让它发挥作用。

只有两个障碍:

  • thread::spawn将不允许分享参考
  • aliveloop让您分享此设计

解决方案有两个方面:

  • 在控制器(Timer)和工作人员(关闭)
  • 之间拆分
  • 使用Arc在两者之间共享状态,因为禁止引用

以下是玩具的最小例子:

use std::{sync, thread, time};
use std::sync::atomic::{AtomicBool, Ordering};

pub struct Timer {
    handle: Option<thread::JoinHandle<()>>,
    alive: sync::Arc<AtomicBool>,
}

impl Timer {
    pub fn new() -> Timer {
        Timer {
            handle: None,
            alive: sync::Arc::new(AtomicBool::new(false)),
        }
    }

    pub fn start<F>(&mut self, fun: F)
        where F: 'static + Send + FnMut() -> ()
    {
        self.alive.store(true, Ordering::SeqCst);

        let alive = self.alive.clone();

        self.handle = Some(thread::spawn(move || {
            let mut fun = fun;
            while alive.load(Ordering::SeqCst) {
                fun();
                thread::sleep(time::Duration::from_millis(10));
            }
        }));
    }

    pub fn stop(&mut self) {
        self.alive.store(false, Ordering::SeqCst);
        self.handle
            .take().expect("Called stop on non-running thread")
            .join().expect("Could not join spawned thread");
    }
}

fn main() {
    let mut timer = Timer::new();
    timer.start(|| println!("Hello, World!") );

    println!("Feeling sleepy...");
    thread::sleep(time::Duration::from_millis(100));

    println!("Time for dinner!");
    timer.stop();
}

我邀请您一次戳一个洞(即,更改一个与您的示例不同的内容,检查错误消息,并尝试了解差异是如何解决的)。

在操场上,它为我打印:

Feeling sleepy...
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Hello, World!
Time for dinner!

虽然我不会依赖(1)"Hello, World!"出现的次数和(2)"Feeling sleepy..."出现的次数。

该死的,Atomic详细...我希望有一个get / set SeqCst(更强的排序)。< / em>的