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

问题描述 投票:11回答:2

编者注 - 此示例是在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
    }
});

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

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

multithreading rust
2个回答
16
投票

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

Terminated externally

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

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

Suspending and resuming

我们使用recv()暂停线程,直到某些东西到达通道。为了恢复该线程,您需要通过该频道发送内容;在这种情况下单位价值()。如果通道的发送端被丢弃,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(());
    }
}

Other tools

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


1
投票

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

这是文档中的示例:

use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use 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;
    let mut started = lock.lock().unwrap();
    *started = true;
    // We notify the condvar that the value has changed.
    cvar.notify_one();
});

// wait for the thread to start up
let &(ref lock, ref cvar) = &*pair;
let mut started = lock.lock().unwrap();
// as long as the value inside the `Mutex` is false, we wait
loop {
    let result = cvar.wait_timeout(started, Duration::from_millis(10)).unwrap();
    // 10 milliseconds have passed, or maybe the value changed!
    started = result.0;
    if *started == true {
        // We received the notification and the value has been updated, we can leave.
        break
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.