如何使线程出现紧急情况以立即终止主线程?

Gre*_*ber 5 rust

在Rust中,紧急情况会终止当前线程,但不会发送回主线程。我们被告知的解决方案是使用join。但是,这会阻塞当前正在执行的线程。因此,如果我的主线程产生了2个线程,那么我将无法同时加入这两个线程并立即感到恐慌。

let jh1 = thread::spawn(|| { println!("thread 1"); sleep(1000000); };
let jh2 = thread::spawn(|| { panic!("thread 2") };
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,如果我在线程1上然后在线程2上加入,我将在等待1之前从任一线程收到恐慌

尽管在某些情况下我希望当前的行为,但我的目标是默认使用Go的行为,在该行为中我可以生成线程并使它在该线程上处于紧急状态,然后立即结束主线程。(Go规范还记录了一个protect功能,因此很容易在Go中实现Rust行为)。

Vla*_*eev 13

针对 Rust 1.10+ 更新,请参阅答案的先前版本的修订历史

好点,在 go 中,主线程没有解开,程序只是崩溃,但报告了最初的恐慌。这实际上是我想要的行为(尽管理想情况下资源会在任何地方得到正确清理)。

这可以通过最近稳定的std::panic::set_hook()功能实现。有了它,你可以设置一个钩子来打印恐慌信息,然后退出整个过程,就像这样:

use std::thread;
use std::panic;
use std::process;

fn main() {
    // take_hook() returns the default hook in case when a custom one is not set
    let orig_hook = panic::take_hook();
    panic::set_hook(Box::new(move |panic_info| {
        // invoke the default handler and exit the process
        orig_hook(panic_info);
        process::exit(1);
    }));

    thread::spawn(move || {
        panic!("something bad happened");
    }).join();

    // this line won't ever be invoked because of process::exit()
    println!("Won't be printed");
}
Run Code Online (Sandbox Code Playgroud)

尝试对set_hook()调用进行注释,您将看到该println!()行已被执行。

但是,由于使用了 ,这种方法process::exit()将不允许释放其他线程分配的资源。事实上,我不确定 Go 运行时是否也允许这样做;它很可能使用与中止过程相同的方法。


小智 9

当任何线程恐慌时,我试图强制我的代码停止处理。不使用不稳定特性的唯一或多或少清晰的解决方案是使用Drop在某些结构上实现的特征。这可能会导致资源泄漏,但在我的情况下,我对此没有意见。

use std::process;
use std::thread;
use std::time::Duration;


static THREAD_ERROR_CODE: i32 = 0x1;
static NUM_THREADS: u32 = 17;
static PROBE_SLEEP_MILLIS: u64 = 500;

struct PoisonPill;

impl Drop for PoisonPill {
    fn drop(&mut self) {
        if thread::panicking() {
            println!("dropped while unwinding");
            process::exit(THREAD_ERROR_CODE);
        }
    }
}

fn main() {
    let mut thread_handles = vec![];

    for i in 0..NUM_THREADS {
        thread_handles.push(thread::spawn(move || {
            let b = PoisonPill;
            thread::sleep(Duration::from_millis(PROBE_SLEEP_MILLIS));
            if i % 2 == 0 {
                println!("kill {}", i);
                panic!();
            }
            println!("this is thread number {}", i);
        }));
    }

    for handle in thread_handles {
        let _ = handle.join();
    }
}
Run Code Online (Sandbox Code Playgroud)

无论如何b = PoisonPill离开它的范围, normal 或 after panic!,它的Drop方法都会启动。您可以区分调用者是否使用恐慌thread::panicking并采取一些行动 - 在我的情况下杀死进程。


Jak*_*kob 6

看起来在任何线程中出现恐慌时退出整个过程现在(rust 1.62)就像将其添加到 Cargo.toml 一样简单:

[profile.release]
panic = 'abort'

[profile.dev]
panic = 'abort'
Run Code Online (Sandbox Code Playgroud)

线程中的恐慌看起来像这样,退出代码为 134:

thread '<unnamed>' panicked at 'panic in thread', src/main.rs:5:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)