gpt4 book ai didi

rust - std::thread::JoinHandle::join 如何捕获 panic ?

转载 作者:行者123 更新时间:2023-12-04 14:55:05 27 4
gpt4 key购买 nike

只有当 dyn FnUnwindSafe + RefUnwindSafe 时,下面的代码才能编译,因为 panic::catch_unwind 要求它能够捕获 panic 。

use std::panic;
use std::panic::{UnwindSafe, RefUnwindSafe};

fn launch_closure(f: Box<dyn Fn() + UnwindSafe + RefUnwindSafe>) {
let result = panic::catch_unwind(|| {
f();
});
}

然而,std::thread::JoinHandle::join即使线程关闭不是UnwindSafe + RefUnwindSafe,函数也能够捕获 panic :

If the child thread panics, Err is returned with the parameter givento panic!.

如何?

我想知道我的闭包是否发生 panic ,但是 UnwindSafe + RefUnwindSafe 限制太多,例如我不能使用 CondVar

Playground

最佳答案

thread::spawn wraps the closure in an AssertUnwindSafe告诉编译器它知道给定的闭包是展开安全的:

let try_result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
crate::sys_common::backtrace::__rust_begin_short_backtrace(f)
}));

那么,什么是展开安全性以及 thread::spawn 如何做出该断言?

来自 UnwindSafe 的文档:

In Rust a function can “return” early if it either panics or calls a function which transitively panics. This sort of control flow is not always anticipated, and has the possibility of causing subtle bugs through a combination of two critical components:

  1. A data structure is in a temporarily invalid state when the thread panics.
  2. This broken invariant is then later observed.

如果这两个都为真,则类型展开安全。

MutexRwLock 这样的类型 展开安全的,因为它们使用中毒来保护您免受损坏的不变量。如果在另一个锁定了 Mutex 的线程中发生 panic ,那么它就会中毒,您必须显式调用 PoisonError::into_inner 来访问可能不一致的数据。如果您通过假设有毒的互斥量而导致错误,那么这是您自己的责任,Rust 类型系统无法帮助您。

可变引用和具有内部可变性的非共享类型(如 RefCell)不是展开安全的,因为它们不提供此类保护。但是,它们也不是 Sync,因此您不会遇到在另一个线程持有引用而 panic 后使用它们的情况。

最后一 block 拼图是 thread::spawn 创建了一个新的线程栈。这意味着它可以保证闭包首先在堆栈中被调用,因此在捕获到 panic 后,与闭包位于同一线程中的任何东西都无法访问其环境。

虽然 thread::spawn 不能保证闭包在一般情况下是展开安全的,但它知道:

  1. 闭包是Send(由它自己的边界),因此它不能包含对非Sync 类型的引用。
  2. std 中的非展开安全类型(可变引用和单元格类型)也不是Sync,这意味着没有任何内容可以从<访问非展开安全类型em>在线程之外
  3. 在捕获 panic 后,与调用闭包的相同线程中的任何内容都无法访问其环境。

所以解开闭包是安全的,因为在 panic 之后不可能无意中观察到损坏的不变量。

闭包当然有可能使用非展开安全但 Sync 的用户定义类型,在这种情况下,这个假设将变为出来是不正确的。然而,这将需要来自第三方包或由与闭包本身相同的作者编写的不安全代码。 unsafe 代码的作者始终有责任确保内存安全。如果另一个线程中的 panic 可能导致 UB,则将类型设置为 Sync 是不合理的。逻辑错误是否可以接受由作者决定,但内存不安全绝不是。


那么...您可以在您的代码中使用相同的技巧吗?不幸的是,你可能做不到。由于您无法控制 launch_closure 的调用者,因此无法保证 panic 不会导致同一线程中的调用者观察到无效状态。

关于rust - std::thread::JoinHandle::join 如何捕获 panic ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68217544/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com