【发布时间】: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,我预计这将特别令人震惊。
我希望通常的建议是使用new 的nothrowversion 并忘记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是否线程安全的标准就像询问彩虹红色是否代表爱一样。