繁体   English   中英

如何从另一个线程终止或挂起 Rust 线​​程?

[英]How to terminate or suspend a Rust thread from another thread?

编者注——这个例子是在 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
    }
});

根据某些条件一段时间后,我需要从程序的另一部分终止该线程。 换句话说,我想退出无限循环。 我怎样才能正确地做到这一点? 另外,我怎么能挂起这个线程并在以后恢复它?

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

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

外部终止

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

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()挂起线程,直到有东西到达通道。 为了恢复线程,你需要通过通道发送一些东西; 在这种情况下,单位值() 如果信道的发送端被丢弃, 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(());
    }
}

其他工具

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

理想的解决方案是Condvar 正如@Vladimir Matveev指出的,您可以在std::sync module使用wait_timeout

这是文档中的示例:

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

我自己已经多次回到这个问题,这就是我认为解决 OP 的意图和其他人让线程自行停止的最佳实践。 基于公认的答案, Crossbeam是 mpsc 的一个很好的升级,它允许克隆和移动消息端点。 它还具有方便的滴答功能。 这里真正的重点是它具有非阻塞的 try_recv()。

我不确定将消息检查器放在像这样的操作循环中间会有多普遍的用处。 我还没有发现 Actix(或以前的 Akka)可以真正停止一个线程,而无需 - 如上所述 - 让线程自行完成。 所以这就是我现在正在使用的(这里对更正持开放态度,仍在学习自己)。

// Cargo.toml:
// [dependencies]
// crossbeam-channel = "0.4.4"

use crossbeam_channel::{Sender, Receiver, unbounded, tick};
use std::time::{Duration, Instant};

fn main() {
    let (tx, rx):(Sender<String>, Receiver<String>) = unbounded();
    let rx2 = rx.clone();

    // crossbeam allows clone and move of receiver
    std::thread::spawn(move || {
        // OP:
        // let mut timer = Timer::new().unwrap();
        // let periodic = timer.periodic(Duration::milliseconds(200));

        let ticker: Receiver<Instant> = tick(std::time::Duration::from_millis(500));

        loop {
            // OP:
            // periodic.recv();
            crossbeam_channel::select! {
                recv(ticker) -> _ => {

                    // OP: Do my work here
                    println!("Hello, work.");

                    // Comms Check: keep doing work?
                    // try_recv is non-blocking
                    // rx, the single consumer is clone-able in crossbeam
                    let try_result = rx2.try_recv();
                    match try_result {
                        Err(_e) => {},
                        Ok(msg) => {
                            match msg.as_str() {
                                "END_THE_WORLD" => {
                                    println!("Ending the world.");
                                    break;
                                },
                                _ => {},
                            }
                        },
                        _ => {}
                    }
                }
            }
        }
    });

    // let work continue for 10 seconds then tell that thread to end.
    std::thread::sleep(std::time::Duration::from_secs(10));
    println!("Goodbye, world.");
    tx.send("END_THE_WORLD".to_string());
}

使用字符串作为消息设备有点令人畏惧——对我来说。 可以在枚举中执行其他挂起和重新启动的操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM