【问题标题】:Does set_new_handler store new_handler in thread local storage?set_new_handler 是否将 new_handler 存储在线程本地存储中?
【发布时间】:2019-09-07 21:38:58
【问题描述】:

有人问set_new_handler 在多线程环境中是否安全。我想知道C++ 11 及更高版本的C++ 标准库实现是否使用thread_local 功能来存储std::new_handler。如果实现不使用thread_local,为什么不呢?这似乎使该功能在多线程程序中更加强大。

我也不太明白set_new_handler 将如何用于重载new 并设置std::new_handler 并创建(并拥有对)一个也重载new 并设置自己的对象的类std::new_handler。如果拥有类具有获取/释放内存的选项,而拥有的对象决定调用 abort/terminate,我预计这将特别令人震惊。

我希望通常的建议是使用newnothrowversion 并忘记set_new_handler。如果这是一个普遍的观点,为什么 set_new_handler 不是标准库中已弃用的功能?

【问题讨论】:

  • 你的问题真的没有意义。 set_new_handler 设置默认内存分配器失败时的处理程序。它不应该为每个线程设置它,我看不出这会如何影响“在多线程环境中”程序的健壮性。同样重要的是,set_new_handler 基本上与重载new 的类无关。新的处理程序不限于特定的类,set_new_handler 只对默认分配器有支配权,而不是你自己覆盖的那些。
  • Scott Meyers 在 Effective C++ 中的第 49 条中展示了一个示例,说明如何在类 C 的 new 运算符重载的上下文中更改 std::new_handler。基本模式是替换新重载顶部的 std::new_handler,并在退出时恢复其状态(使用 RAII 句柄的最佳实践)。因此,在多线程程序中,如果多个类尝试这样做并且新的重载没有以某种方式全局同步,这肯定会导致意外行为。
  • Effective C++ 早于 C++11,因此早于线程作为 C++ 核心语言和库中的概念。在 C++11 之前,new 关于线程的行为不是未定义的;它甚至都不是一件事,因为“线程”的概念甚至没有被标准考虑。因此,一旦您开始考虑线程,一个从未考虑线程的项目突然变得不可行也就不足为奇了。
  • @NicolBolas 你能解释一下“Pre-C++11,new 关于线程的行为不是未定义的;它甚至不是一件事”是什么意思吗?我知道在 C++11 之前的时代编写的许多 C++ 代码使用 POSIX 线程或 Boost::threads。早在 C++0x/11 之前,全局 ::operator new 在许多平台/编译器上的多线程代码中都是安全的。也就是说,我不同意 std::set_new_handler 可能对线程从来都不安全(并且可能仍然不安全),因此我试图澄清它的实现!
  • "我知道在 C++11 之前的时代编写的许多 C++ 代码使用 POSIX 线程或 Boost::threads。" 当然,但就标准 担心,threading was not even a question you could ask。询问operator new 是否线程安全的标准就像询问彩虹红色是否代表爱一样。

标签: c++ c++11 memory


【解决方案1】:

new_handler 不是,也从未预料到是线程本地构造。它始终是全球性的,这是设计使然。

新的处理程序只需要设置一次就可以了;它不适用于单个执行线程本地的临时更改。您可以为自己构建这样的工具,方法是创建一个新的处理程序,将其操作推迟到您的代码可以设置的thread_local 变量。但 C++ 新的处理程序特性本身并不适用于此类用例。

现在,可以说这是一项遗留决定。 C++98/03的内存模型没有考虑线程的可能性。因此,任何 C++11 之前的线程实现基本上都是自己决定哪些操作是线程安全的,哪些是线程本地的,等等。决定将 new-handler 实现为全局构造的实现与使其成为每个线程的实现一样正确。事实上,实现可能在某个时候选择了全局选项,而 C++11 刚刚采用了它。并且将其更改为线程本地会破坏人们的代码。

但与此同时,新处理程序的重点是允许用户更改分配失败的默认处理方式。默认错误处理不是线程本地的概念;这是整个程序通用的东西。与终止函数等非常相似,预计会使用一个。

简单地说,使用类本地新处理程序的代码在某种程度上总是错误,即使在 C++98/03 时代也是如此。只是在 C++11 标准化线程之前,它是你可以摆脱的(至少就标准而言)。它并没有被弃用,因为它对于它的预期目的很有用nothrow 只在本地有用; set_new_handler 在全球范围内很有用。

【讨论】:

  • @Quentin:OP 特别提到了 operator new 重载更改新处理程序的类。这就是我说“本地类”的原因。
  • 糟糕,我的错! operator new 部分在我阅读您的答案时从我脑海中溜走,我以为您打错了。
猜你喜欢
  • 1970-01-01
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
  • 2023-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多