rust 我怎样才能在线程上引起紧急情况以立即结束主线程?

vlf7wbxs  于 2022-12-23  发布在  其他
关注(0)|答案(3)|浏览(155)

在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行为很容易)。

r1zhe5dt

r1zhe5dt1#

针对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");
}

尝试注解掉set_hook()调用,您将看到println!()行得到执行。
但是,由于使用了process::exit(),这种方法不允许释放其他线程分配的资源,事实上,我不确定Go语言运行时是否允许这样做;则它很可能使用与中止该过程相同的方法。

nlejzf6q

nlejzf6q2#

我试图强迫我的代码在任何线程出现异常时停止处理。唯一不使用不稳定特性的或多或少明确的解决方案是使用在某个结构上实现的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来区分调用者是否惊慌失措,并采取一些措施-在我的例子中是终止进程。

czfnxgou

czfnxgou3#

看起来像退出整个过程中的恐慌在任何线程现在( rust 1.62)一样简单,添加到您的货物.toml:

[profile.release]
panic = 'abort'

[profile.dev]
panic = 'abort'

线程中的死机如下所示,退出代码为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)

相关问题