运行可中断的Rust程序,它产生线程

时间:2016-10-13 17:25:23

标签: multithreading rust signals

我正在尝试编写一个程序来生成一堆线程,然后在最后加入线程。我希望它是可中断的,因为我的计划是在UNIX服务中使它成为一个不断运行的程序。

我们的想法是worker_pool将包含所有已生成的线程,因此可以随时调用terminate来收集它们。

我似乎找不到使用chan_select包来执行此操作的方法,因为这需要我首先生成一个线程来生成我的子线程,一旦我这样做,我就不能再使用{{1}在中断时加入线程时的变量,因为必须将其移出主循环。如果你注释掉中断中终止工人的行,它就会编译。

我有点沮丧,因为这在C中真的很容易。我可以设置一个静态指针,但是当我在Rust中尝试这样做时我得到一个错误因为我正在使用一个向量线程,我无法初始化为静态中的空向量。我知道在中断代码中加入worker是安全的,因为执行在这里停止等待信号。

也许有更好的方法来进行信号处理,或者我可能会遗漏一些我可以做的事情。

错误和代码如下:

worker_pool

main.rs

MacBook8088:video_ingest pjohnson$ cargo run
   Compiling video_ingest v0.1.0 (file:///Users/pjohnson/projects/video_ingest)
error[E0382]: use of moved value: `worker_pool`
  --> src/main.rs:30:13
   |
24 |     thread::spawn(move || run(sdone, &mut worker_pool));
   |                   ------- value moved (into closure) here
...
30 |             worker_pool.terminate();
   |             ^^^^^^^^^^^ value used here after move
<chan macros>:42:47: 43:23 note: in this expansion of chan_select! (defined in <chan macros>)
src/main.rs:27:5: 35:6 note: in this expansion of chan_select! (defined in <chan macros>)
   |
   = note: move occurs because `worker_pool` has type `video_ingest::WorkerPool`, which does not implement the `Copy` trait

lib.rs

#[macro_use]
extern crate chan;
extern crate chan_signal;
extern crate video_ingest;

use chan_signal::Signal;
use video_ingest::WorkerPool;
use std::thread;
use std::ptr;

///
/// Starts processing
/// 
fn main() {
    let mut worker_pool = WorkerPool { join_handles: vec![] };

    // Signal gets a value when the OS sent a INT or TERM signal.
    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);

    // When our work is complete, send a sentinel value on `sdone`.
    let (sdone, rdone) = chan::sync(0);

    // Run work.
    thread::spawn(move || run(sdone, &mut worker_pool));

    // Wait for a signal or for work to be done.
    chan_select! {
        signal.recv() -> signal => {
            println!("received signal: {:?}", signal);
            worker_pool.terminate(); // <-- Comment out to compile
        },
        rdone.recv() => {
            println!("Program completed normally.");
        }
    }
}

fn run(sdone: chan::Sender<()>, worker_pool: &mut WorkerPool)  {
    loop {
        worker_pool.ingest();
        worker_pool.terminate();
    }
}

1 个答案:

答案 0 :(得分:1)

  

terminate可以随时调用来收集它们。

     

我不想阻止线程;我想用join收集它们。我同意阻止他们不是一个好主意。

这两个陈述对我来说没有意义。您只能在完成时加入 。 “可中断”和“在任何时候”这个词意味着您可以尝试在停止线程时进行某些处理。你想要哪种行为?

如果您希望能够停止已部分完成的线程,则必须增强代码以检查它是否应该提前退出。这通常很复杂,因为你正在做一些你无法控制的大计算。理想情况下,您将其分解为块并经常检查您的退出标志。例如,通过视频工作,您可以检查每一帧。然后响应延迟大致是处理帧的时间。

  

这在C中真的很容易。

这很容易做错误。例如,当前呈现的代码尝试从两个不同的线程执行到池的突变而没有任何类型的同步。这是制作破解,难以调试的代码的可靠方法。

  

//使用9个线程作为示例。

0..10创建10个主题。

无论如何,似乎缺少的知识是ArcMutexArc允许在线程之间共享单个项的所有权,Mutex允许线程之间的运行时可变借用。

#[macro_use]
extern crate chan;
extern crate chan_signal;

use chan_signal::Signal;
use std::thread::{self, JoinHandle};
use std::sync::{Arc, Mutex};

fn main() {
    let worker_pool = Arc::new(Mutex::new(WorkerPool::new()));

    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);

    let (work_done_tx, work_done_rx) = chan::sync(0);

    let worker_pool_clone = worker_pool.clone();
    thread::spawn(move || run(work_done_tx, worker_pool_clone));

    // Wait for a signal or for work to be done.
    chan_select! {
        signal.recv() -> signal => {
            println!("received signal: {:?}", signal);
            let mut pool = worker_pool.lock().expect("Unable to lock the pool");
            pool.terminate();
        },
        work_done_rx.recv() => {
            println!("Program completed normally.");
        }
    }
}

fn run(_work_done_tx: chan::Sender<()>, worker_pool: Arc<Mutex<WorkerPool>>)  {
    loop {
        let mut worker_pool = worker_pool.lock().expect("Unable to lock the pool");
        worker_pool.ingest();
        worker_pool.terminate();
    }
}

pub struct WorkerPool {
    join_handles: Vec<JoinHandle<()>>,
}

impl WorkerPool {
    pub fn new() -> Self {
        WorkerPool {
            join_handles: vec![],
        }
    }

    pub fn ingest(&mut self) {
        self.join_handles.extend(
            (0..10).map(|i| {
                thread::spawn(move || {
                    println!("Getting videos for thread {}", i);
                })
            })
        )
    }

    pub fn terminate(&mut self) {
        for handle in self.join_handles.drain(..) {
            handle.join().expect("Unable to join thread")
        }
    }
}

请注意程序逻辑本身仍然很差;即使发送了中断,loop 中的run仍会继续执行。主线程将锁定互斥锁,加入所有当前线程 1 ,解锁互斥锁并退出程序。但是,循环可以在主线程退出之前锁定互斥锁并开始处理一些新数据!然后程序在处理过程中退出。它几乎和你根本没有处理中断一样。

1 :哈哈,欺骗了你!那时没有正在运行的线程。由于互斥锁被锁定为整个loop,因此唯一可以进行另一次锁定的是循环重置时。但是,由于循环中的最后一条指令是连接所有线程,因此将不再运行。

  

我不希望程序在所有线程完成之前终止。

也许这是一个减少问题的神器,但我看不出无限循环如何退出,所以“我已经完成”的频道似乎是多余的。

我可能只是在收到中断时添加一个标记“请停止”。然后我会检查它而不是无限循环,并在退出程序之前等待正在运行的线程完成。

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

fn main() {
    let worker_pool = WorkerPool::new();

    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
    let please_stop = Arc::new(AtomicBool::new(false));

    let threads_please_stop = please_stop.clone();
    let runner = thread::spawn(|| run(threads_please_stop, worker_pool));

    // Wait for a signal
    chan_select! {
        signal.recv() -> signal => {
            println!("received signal: {:?}", signal);
            please_stop.store(true, Ordering::SeqCst);
        },
    }

    runner.join().expect("Unable to join runner thread");
}

fn run(please_stop: Arc<AtomicBool>, mut worker_pool: WorkerPool)  {
    while !please_stop.load(Ordering::SeqCst) {
        worker_pool.ingest();
        worker_pool.terminate();
    }
}