Как проверить, закончил ли поток в Rust?

Когда я spawn нить в Rust я получаю JoinHandle, что хорошо для... присоединения (блокирующая операция), и не более того. Как я могу проверить, вышел ли дочерний поток (т.е. JoinHandle.join() не будет блокировать) из родительского потока? Бонусные баллы, если вы знаете, как убить дочернюю нить.

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

5 ответов

Начиная с Rust 1.7, в стандартной библиотеке нет API для проверки, вышел ли дочерний поток без блокировки.

Портативный обходной путь - использовать каналы для отправки сообщения от ребенка родителю, чтобы сообщить, что ребенок собирается выйти. Receiver имеет неблокирующую try_recv метод. когда try_recv получает сообщение, вы можете использовать join() на JoinHandle чтобы получить результат потока.

Существуют также нестабильные характерные для платформы характеристики расширения, которые позволяют вам получить необработанный дескриптор потока. Затем вам придется написать специфичный для платформы код, чтобы проверить, завершился ли поток или нет.

Если вы считаете, что эта функция должна быть в стандартной библиотеке Rust, вы можете отправить RFC (обязательно сначала прочтите README!).

Бонусные баллы, если вы знаете, как убить дочернюю нить.

Потоки в Rust реализованы с использованием собственных потоков ОС. Несмотря на то, что операционная система может предоставить способ уничтожения потока, это плохая идея, поскольку ресурсы, выделенные потоком, не будут очищены до завершения процесса.

Это возможно, друзья. Используйте refcounters, которые Rust упадет подряд или паникует. 100% безопасно. Пример:

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

fn main() {
    // Play with this flag
    let fatal_flag = true;
    let do_stop = true;

    let working = Arc::new(AtomicBool::new(true));
    let control = Arc::downgrade(&working);

    thread::spawn(move || {
        while (*working).load(Ordering::Relaxed) {
            if fatal_flag {
                panic!("Oh, my God!");
            } else {
                thread::sleep(Duration::from_millis(20));
                println!("I'm alive!");
            }
        }
    });

    thread::sleep(Duration::from_millis(50));

    // To stop thread
    if do_stop {
        match control.upgrade() {
            Some(working) => (*working).store(false, Ordering::Relaxed),
            None => println!("Sorry, but thread died already."),
        }
    }

    thread::sleep(Duration::from_millis(50));

    // To check it's alive / died
    match control.upgrade() {
        Some(_) => println!("Thread alive!"),
        None => println!("Thread ends!"),
    }
}

Суть: https://gist.github.com/DenisKolodin/edea80f2f5becb86f718c330219178e2

На игровой площадке: https://play.rust-lang.org/?gist=9a0cf161ba0bbffe3824b9db4308e1fb&version=stable&backtrace=0

UPD: я создал thread-control ящик, который реализует этот подход: https://github.com/DenisKolodin/thread-control

Краткий ответ пока невозможен. Но это не тот вопрос, который действительно нужно решать.

Бонусные баллы, если вы знаете, как убить дочернюю нить.

НИКОГДА

Даже в тех языках, которые поддерживают уничтожение потоков ( см. Здесь Java), рекомендуется этого не делать.

Выполнение потока обычно кодируется с явными точками взаимодействия, и часто существуют неявные предположения, что никакого другого прерывания не произойдет.

Самым вопиющим примером являются, конечно, ресурсы: наивный метод "kill" должен был бы остановить выполнение потока; это будет означать не выпускать какой-либо ресурс. Вы можете думать о памяти, это меньше всего беспокоит вас. Вообразите вместо этого все Mutex которые не разблокированы и создадут тупики позже...

Другим вариантом будет ввести panic в потоке, что приведет к размотке. Тем не менее, вы не можете просто начать раскручивать в любой момент! Программа должна определить безопасные точки, в которых panic будет гарантированно безопасным (внедрение его в любой другой точке означает потенциальное повреждение общих объектов); как определить такие безопасные точки и ввести panic существует проблема открытых исследований на родных языках, особенно тех, которые выполняются в системах W^X (где страницы памяти либо записываемые, либо исполняемые, но никогда не оба).

Таким образом, не существует известного способа безопасного (как по памяти, так и по функциональности) уничтожения потока.

Начиная с rust 1.61.0, есть is_finishedметод.

https://doc.rust-lang.org/stable/std/thread/struct.JoinHandle.html#method.is_finished

Я думаю, что Arc можно использовать для решения этой проблемы. Если поток завершается, счетчик ссылок уменьшается на единицу.

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