设计帮助:结构内的线程



我是 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
    }
}

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

我有一种预感,我需要更多地了解内部可变性,但我想在进入更多的兔子洞之前,我会寻求设计指导。

我认为你已经非常接近让它工作了。

只有两个障碍:

  • 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/setSeqCst(更强的排序(可用。

最新更新