【问题标题】:What is the difference between std::quick_exit and std::abort and why was std::quick_exit needed?std::quick_exit 和 std::abort 有什么区别,为什么需要 std::quick_exit?
【发布时间】:2012-04-03 05:58:52
【问题描述】:

C++11 引入了一种完成程序执行的新方法——std::quick_exit

引用 N3242 18.5(第 461 页):

[[noreturn]] void quick_exit(int status) noexcept;

效果:调用at_quick_exit注册的函数被调用 以他们注册的相反顺序,除了一个函数 应在任何先前注册的函数之后调用 在注册时已经被调用。对象不得 由于调用quick_exit 而被销毁。如果控制离开 quick_exit 调用的注册函数,因为该函数没有 为抛出的异常提供处理程序,应调用terminate()。 [注意:at_quick_exit 可能会从不同的 线程比注册它的线程,所以注册函数应该 不依赖于具有线程存储持续时间的对象的身份。 — end note ] 调用注册函数后,quick_exit 会调用 _Exit(status)。 [ 注意:标准文件缓冲区不会被刷新。参见:ISO C 7.20.4.4。 ——尾注]

由于std::abort(void)std::_Exit(int status) 的定义仅在将状态传递给父进程的能力上有所不同,因此提出了我的问题。

这是否意味着std::quick_exitstd::abort 在语义上的唯一区别是std::quick_exit 调用使用std::at_quick_exit 注册的函数并允许设置返回状态?

引入此功能的基本原理是什么?

【问题讨论】:

    标签: c++ c++11 language-lawyer


    【解决方案1】:

    available here 写的不错,我就总结一下。添加此功能是为了专门解决使用线程时干净地结束程序的困难。本质上,退出是由高度异步的事件启动的,用户关闭用户界面,管理员关闭机器等等。这与程序启动的线程的状态无关,它们几乎总是处于高度不可预测的状态。

    在理想情况下,程序的 main() 函数要求线程退出,通常通过发出事件信号,等待线程结束,然后退出 main() 以通过 exit() 彻底关闭。然而,这个理想非常很难实现。一个线程可能深埋在系统调用中,例如,等待一些 I/O 完成。或者它正在阻塞需要由另一个线程以正确顺序发出信号的同步对象。结果很少令人愉快,真正的程序经常在退出时陷入僵局。或者当关闭命令意外时崩溃。

    对于这个问题有一个简单且非常诱人的解决方法:调用 _exit() 代替。 Kaboom,程序结束,操作系统扫出弹片。但显然根本没有进行任何清理,有时会因为文件写了一半或数据库事务不完整等工件而变得非常混乱。

    std::quick_exit() 提供了替代方案。与 _exit() 类似,但仍然可以选择执行一些代码,无论在 at_quick_exit 中注册了什么。

    【讨论】:

    • 此外,由于abort 发出SIGABRT 的信号,调用abort 通常会导致(尽管这是可配置的)在*nix 上的核心转储,或Windows 上的弹出窗口(例如程序已停止工作,关闭/调试)。仅在因意外情况而终止并且您希望使用核心转储/小型转储来诊断意外情况的原因时使用abort
    • 我一直希望我的眼睛能在两个大量使用反引号和粗体字的句子中分辨出什么是真正重要的。不,我需要一双新的眼睛。购物建议表示赞赏。
    【解决方案2】:

    std::quick_exit 的基本原理在N1327N2440 中进行了讨论。 quick_exit_Exitexit 之间的主要区别在于静态析构函数的处理和将关键信息刷新到稳定存储:

    • std::_Exit:不执行静态析构函数或刷新关键 IO。
    • std::exit:执行静态析构函数并刷新关键 IO。
    • std::quick_exit:不执行静态析构函数,但会刷新关键 IO。

    (如前所述,std::abort 只是发送SIGABRT。)

    【讨论】:

    • 我在标准中没有看到 std::quick_exit 刷新关键 IO 的任何证据。事实上,标准说“注意:标准文件缓冲区没有被刷新”。您认为 std::quick_exit 刷新关键 IO 的依据是什么,就此而言,您所说的“关键” IO 是什么意思?
    • @KnowItAllWannabe:他将关键定义为“您足够关心它以注册 at_quick_exit 处理程序:
    【解决方案3】:

    std::abort 将终止您的应用程序,而不调用使用“at_​​exit/at_quick_exit”注册的任何函数。另一方面,正如您所指出的,std::quick_exit 将调用使用std::at_quick_exit 注册的函数。

    std::abort 通常中止您的应用程序,当一些异常情况发生并且您的应用程序必须在不进行任何清理的情况下关闭时应该调用它。来自std::abort 文档:

    导致程序异常终止,除非 SIGABRT 被 传递给信号的信号处理程序并且该处理程序不返回。

    当你想进行一些清理时,std::quick_exit 会更合适。最后一个函数还可以让您优雅地停止应用程序,因为它最终调用std::_Exit 而不是发出类似std::abort 的信号(发出信号SIGABRT,使应用程序异常停止)。

    std::exit 允许您优雅地退出应用程序,同时仍然清理自动、线程本地和静态变量。 std::quick_exit 没有。这就是为什么它的名字中有一个“quick_”,因为它跳过了清理阶段,所以速度更快。

    因此,这两个函数之间存在实际的语义差异。一个异常停止应用程序,另一个执行优雅退出,允许您进行一些清理工作。

    【讨论】:

    • 这仍然没有解决 - 为什么允许在“优雅地”终止时进行一些清理而不是全部?我从未在真正的代码中见过quick_exit,但如果我在代码审查中得到它,我可能会很想要求将其彻底清理或什么都不做。是否有任何关于此的 WG21 消息来源?
    • 如果你想彻底清理,那么你可以使用“exit”。 quick_exit 更快,因为它不调用任何自动、线程局部和静态变量的析构函数。我认为将 std::quick_exit 与 std::exit 进行比较会更好,因为它们都允许用户执行优雅关闭,而 std::abort 基本上会异常终止您的应用程序。
    • C++ 析构函数通常会做一些在进程退出时不需要的事情(比如空闲内存)(因为操作系统会这样做)。我猜quick_exit 允许你做一些清理工作,但剩下的留给操作系统。
    • std::exit 不会对具有自动持续时间的变量执行任何操作。从最终在main 中捕获的异常中展开堆栈更合适。
    猜你喜欢
    • 2010-12-12
    • 2014-07-22
    • 2023-04-09
    • 2020-10-16
    • 2020-09-30
    • 2021-10-16
    • 2015-07-18
    • 2019-02-26
    • 2013-02-07
    相关资源
    最近更新 更多