Каков стандартный способ получить поток Rust от операций блокировки?

Исходя из 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, я нахожу множество, казалось бы, специальных решений, упомянутых как mio, tokio, Я также нахожу ErrorKind::Interrupted и пытался получить это ErrorKind с отправкой SIGINT в нить, но нить, кажется, сразу же умирает, не оставляя (назад) след.

Вот код, который я использовал (примечание: еще не очень хорошо разбирался в 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)

Каковы будут соответствующие средства для передачи сообщения потоку, как Thread.interrupt() на Яве, что пора собираться и идти домой?

1 ответ

Решение

Там нет такой вещи. Блокировка означает блокировку.

Вместо этого вы сознательно используете неблокирующие инструменты. Вот где приходят библиотеки, такие как mio или futures - они обрабатывают архитектуру склеивания всех этих неблокирующих, асинхронных частей.

catch (InterruptedException e)

Ржавчина не имеет исключений. Если вы ожидаете обработать случай сбоя, это лучше представить с 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

Возможно, вы имеете в виду что-то вроде канала MPSC, и в этом случае вы будете использовать такие инструменты, как try_recv,

Смотрите также:

Другие вопросы по тегам