首页 文章

如何从另一个线程终止或暂停Rust线程?

提问于
浏览
10

编者注 - 此示例是在Rust 1.0之前创建的,并且从那时起特定类型已更改或已被删除 . 一般问题和概念仍然有效 .

我已经产生了一个带有无限循环和计时器的线程 .

thread::spawn(|| {
    let mut timer = Timer::new().unwrap();
    let periodic = timer.periodic(Duration::milliseconds(200));
    loop {
        periodic.recv();

        // Do my work here
    }
});

在基于某些条件的时间之后,我需要从程序的另一部分终止该线程 . 换句话说,我想退出无限循环 . 我该怎么做才能正确?另外,我怎么能暂停这个线程并在以后恢复呢?

我试图使用全局不安全标志来打破循环,但我认为这个解决方案看起来不太好 .

2 回答

  • 15

    对于终止和挂起线程,您可以使用通道 .

    外部终止

    在工作循环的每次迭代中,我们检查是否有人通过渠道通知我们 . 如果是,或者如果通道的另一端超出范围,我们就会打破循环 .

    use std::io::{self, BufRead};
    use std::sync::mpsc::{self, TryRecvError};
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        println!("Press enter to terminate the child thread");
        let (tx, rx) = mpsc::channel();
    
        thread::spawn(move || loop {
            println!("Working...");
            thread::sleep(Duration::from_millis(500));
            match rx.try_recv() {
                Ok(_) | Err(TryRecvError::Disconnected) => {
                    println!("Terminating.");
                    break;
                }
                Err(TryRecvError::Empty) => {}
            }
        });
    
        let mut line = String::new();
        let stdin = io::stdin();
        let _ = stdin.lock().read_line(&mut line);
    
        let _ = tx.send(());
    }
    

    暂停和恢复

    我们使用 recv() 暂停线程,直到某些东西到达通道 . 为了恢复该线程,您需要通过该 Channels 发送内容;在这种情况下单位值 () . 如果通道的发送端被丢弃, recv() 将返回 Err(()) - 我们使用它来退出循环 .

    use std::io::{self, BufRead};
    use std::sync::mpsc;
    use std::thread;
    use std::time::Duration;
    
    fn main() {
        println!("Press enter to wake up the child thread");
        let (tx, rx) = mpsc::channel();
        thread::spawn(move || loop {
            println!("Suspending...");
            match rx.recv() {
                Ok(_) => {
                    println!("Working...");
                    thread::sleep(Duration::from_millis(500));
                }
                Err(_) => {
                    println!("Terminating.");
                    break;
                }
            }
        });
    
        let mut line = String::new();
        let stdin = io::stdin();
        for _ in 0..4 {
            let _ = stdin.lock().read_line(&mut line);
            let _ = tx.send(());
        }
    }
    

    其他工具

    Channels 是执行这些任务的最简单,最自然(IMO)的方式,但不是最有效的方法 . 您可以在std::sync模块中找到其他并发原语 . 它们属于比通道更低的级别,但在特定任务中可以更有效 .

  • 0

    理想的解决方案是Condvar . 您可以在 std::sync module 中使用wait_timeout,如pointed out by @Vladimir Matveev .

    这是文档中的示例:

    使用std :: sync :: {Arc,Mutex,Condvar};
    使用std :: thread;
    使用std :: time :: Duration;

    let pair = Arc :: new((Mutex :: new(false),Condvar :: new()));
    let pair2 = pair.clone();

    thread :: spawn(move || {
    let&(ref lock,ref cvar)=&* pair2;
    让mut start = lock.lock() . unwrap();

    • started = true;
      //我们通知condvar值已更改 .
      cvar.notify_one();
      });

    //等待线程启动
    let&(ref lock,ref cvar)=&* pair;
    让mut start = lock.lock() . unwrap();
    //只要Mutex中的值为false,我们就等了
    循环{
    let result = cvar.wait_timeout(started,Duration :: from_millis(10)) . unwrap();
    //已经过了10毫秒,或者值可能已经改变了!
    started = result.0;
    if * started == true {
    //我们收到了通知, Value 已经更新,我们可以离开 .
    打破
    }
    }

相关问题