让 Rust 线程摆脱阻塞操作的标准方法是什么?

What is the standard way to get a Rust thread out of blocking operations?

提问人:Harald 提问时间:9/23/2018 最后编辑:Harald 更新时间:7/12/2022 访问量:8714

问:

我来自Java,习惯于沿着以下方式的成语

while (true) {
  try {
    someBlockingOperation();
  } catch (InterruptedException e) {
    Thread.currentThread.interrupt(); // re-set the interrupted flag
    cleanup(); // whatever is necessary
    break;
  }
}

据我所知,这适用于整个 JDK 中任何可能阻塞的内容,例如从文件、套接字、队列甚至 .Thread.sleep()

在阅读如何在 Rust 中完成这项工作时,我发现提到了许多看似特殊的解决方案,例如 、.我也发现并试图通过发送到线程来获得它,但线程似乎立即死亡而没有留下任何(回溯)痕迹。miotokioErrorKind::InterruptedErrorKindSIGINT

这是我使用的代码(注意:还不是很精通 Rust,所以它可能看起来有点奇怪,但它可以运行):

use std::io;
use std::io::Read;
use std::thread;

pub fn main() {
    let sub_thread = thread::spawn(|| {
        let mut buffer = [0; 10];
        loop {
            let d = io::stdin().read(&mut buffer);
            println!("{:?}", d);
            let n = d.unwrap();
            if n == 0 {
                break;
            }
            println!("-> {:?}", &buffer[0..n]);
        }
    });

    sub_thread.join().unwrap();
}

通过“阻塞操作”,我的意思是:

  • 套接字 IO
  • 文件 IO
  • 队列 IO(还不确定队列在 Rust 中的位置)

像在 Java 中一样,向线程发出信号,表明是时候收拾行李回家了?Thread.interrupt()

多线程 Rust 终止

评论


答:

14赞 Shepmaster 9/23/2018 #1

没有这样的事情。阻塞意味着阻塞。

相反,您故意使用非阻塞工具。这就是像 mio、Tokio 或 futures 这样的库的用武之地——它们处理将所有这些非阻塞、异步片段粘在一起的架构。

catch (InterruptedException e)

Rust 没有例外。如果您希望处理失败情况,最好用 .Result

Thread.interrupt()

除了在线程中设置一些代码可能会检查然后引发异常的标志之外,这实际上不会做任何事情。您可以自己构建相同的结构。一个简单的实现:

use std::{
    sync::{
        atomic::{AtomicBool, Ordering},
        Arc,
    },
    thread,
    time::Duration,
};

fn main() {
    let please_stop = Arc::new(AtomicBool::new(false));

    let t = thread::spawn({
        let should_i_stop = please_stop.clone();
        move || {
            while !should_i_stop.load(Ordering::SeqCst) {
                thread::sleep(Duration::from_millis(100));
                println!("Sleeping");
            }
        }
    });

    thread::sleep(Duration::from_secs(1));
    please_stop.store(true, Ordering::SeqCst);
    t.join().unwrap();
}

据我所知,没有办法打断。文档甚至说:

在 Unix 平台上,由于信号,此函数不会提前返回

插槽 IO

使用 set_nonblocking 等方法将套接字置于非阻塞模式,然后处理 ErrorKind::WouldBlock

另请参阅:

文件 IO

实际上,没有一种很好的跨平台方法来执行异步文件 IO。大多数实现都会启动线程池并在那里执行阻塞操作,通过执行非阻塞操作的内容发送数据。

另请参阅:

队列 IO

也许你的意思是像 MPSC 通道这样的东西,在这种情况下,你会使用像 try_recv 这样的工具。

另请参阅:

评论

0赞 Juraj Michalak 7/12/2022
有了阻塞和中断,情况就更复杂了。但是你的“睡眠”反应令人困惑。 是正常的取消点 (POSIX),另外从“或直到信号到达,这不会被忽略”。看我的帖子: quora.com/...sleepman 3 sleep
1赞 Sebastian Redl 7/12/2022
@JurajMichalak 来自 std::thread::sleep 文档:“在 Unix 平台上,底层系统调用可能会被虚假唤醒或信号处理程序中断。为了确保睡眠至少在指定的持续时间内发生,此函数可能会多次调用该系统调用。