首页 文章

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

提问于
浏览
3

在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 回答

  • 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运行时是否允许这样做;它可能会使用相同的方法来中止流程 .

  • 3

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

    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 恐慌并采取一些行动 - 在我的情况下杀死进程 .

相关问题