如何在线程上引起恐慌立即结束主线程?

时间:2016-03-14 13:22:47

标签: rust

在Rust中,恐慌会终止当前线程,但不会发送回主线程。我们被告知的解决方案是使用join。但是,这会阻止当前正在执行的线程。因此,如果我的主线程产生2个线程,我无法加入它们并立即引起恐慌。

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

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

虽然在某些情况下我希望当前的行为,但我的目标是默认Go的行为,我可以生成一个线程并让它在该线程上发生恐慌,然后立即结束主线程。 (Go规范还记录了protect函数,因此很容易在Go中实现Rust行为。

2 个答案:

答案 0 :(得分:3)

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

这可以通过不稳定的std::panic::set_handler()来实现。您可以设置一个打印恐慌信息的处理程序,然后退出整个过程,如下所示:

#![feature(std_panic, panic_handler)]

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

fn main() {
    // take_handler() returns the default handler in case when a custom one is not set
    let orig_handler = panic::take_handler();
    panic::set_handler(move |panic_info| {
        // invoke the default handler and exit the process
        orig_handler(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");
}

尝试对set_handler()发表评论,您会看到println!()行被执行。

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

答案 1 :(得分:3)

我尝试强制我的代码在任何线程恐慌时停止处理。没有使用不稳定特性的唯一或多或少的清晰解决方案是在某些结构上使用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();
    }
}

无论b = PoisonPill如何离开它的范围,正常或panic!之后,其Drop方法都会启动。您可以区分调用者是否使用thread::panicking恐慌并采取一些措施行动 - 在我的案件中杀死了这个过程。