【问题标题】:Thread safety for overloaded operator new重载运算符 new 的线程安全
【发布时间】:2011-12-06 01:14:02
【问题描述】:

虽然标准不保证new 的线程安全,但大多数多线程操作系统support thread-safe operator new

我正在为我的代码中某些class(比如MyClass)的动态分配实现我自己的内存管理。对于MyClass 的线程安全,我可能不得不使用pthreadboost:: 库。

我认为如果new已经线程安全,那么我可以为MyClass 重载它并利用它的安全性而不必担心使用这些库。

class MyClass {
// data
public:
  void* operator new (size_t);
  void operator delete (void*);
};

对于 C++03 系统/编译器,这是一个公平的假设吗?

编辑:因为我的问题并没有被少数用户关注。我正在详细说明那部分:

如果我有 2 个线程分别执行 new int()new int(),则将返回 2 个唯一的内存地址。现在,在我重载的MyClass::new 中,我没有使用全局::new() 或任何线程库;但是自己的内存管理器(它对线程一无所知)。伪代码:

char pool[BIG_SIZE];
void* MyClass::operator new (size_t size)
{
  // get some memory from 'pool' without using any thread library
  return p;
}

我的假设是,由于全局 ::new 是线程安全的,因此重载的 operator new 也应该是线程安全的。换句话说,编译器应该在遇到new 关键字的任何地方发出与线程安全相关的代码。这是一个正确的假设吗?

【问题讨论】:

  • MyClass::operator new 只是一个语法有些奇怪的常规函数​​。它与任何::operator new 变体无关,也不会神奇地继承它们的线程安全性。
  • 正如您已经说过的那样,大多数编译器确实提供了 new 的线程安全版本,尽管 C++03 标准不保证相同。那么 Q 是什么?您将需要依赖您所针对的编译器的文档,如果您的意思是从可移植性的角度来看,由于标准没有明确要求 new 是线程安全的,严格来说它不是 100% 可移植的,但它仍然是一个公平的假设,给定大多数编译器都实现了它。
  • @Als,在给定的平台中,如果new 是线程安全的,那么假设它的重载版本(对于某些class也是是线程是否公平安全吗?
  • 这完全取决于您在重载新代码时编写的代码。实际上,这适用于任何线程安全的 api/construct。api 或构造本身是线程安全的,但如果它们的使用不是以线程安全的方式完成,则没有实现可以保证它们是线程安全的。

标签: c++ thread-safety operator-overloading new-operator


【解决方案1】:

我认为对于您编辑的问题,答案是肯定的“否”。

您似乎也对新运算符和新表达式感到困惑(没有::new() 这样的东西,只有::new::operator new()),所以也许最好将其分解。

当您编写T * p = new T; 时,其中T 有一个重载的operator-new,然后会发生与以下等价的序列:

void * addr = T::operator new(sizeof(T));  // #1
::new (addr) T;                            // global placement-new

#1 中的函数调用很重要。这个调用是线程安全的吗?好吧,这完全取决于如何定义该函数!标准中没有任何内容可以保证该函数的任何特定行为,毕竟这是一个完全普通的函数。

(新)标准或编译器供应商唯一保证的是默认提供的全局函数void * ::operator new(std::size_t) throw(std::bad_alloc); 是线程安全的。所以如果你用那个写你自己的函数,你没问题;否则你只能靠自己了:

struct Foo
{
  static void * operator new(size_t n) { return ::operator new(n); } // OK
};

struct Bar
{
  static void * operator new(size_t n) { horribly_broken_function(); return 0x0505; }
  // probably not OK
}

【讨论】:

    【解决方案2】:

    来自更新的问题:

    我的假设是,由于 global ::new 是线程安全的,所以这个重载的 operator new 也应该是线程安全的。换句话说,编译器应该在遇到 new 关键字时发出与线程安全相关的代码。这是一个正确的假设吗?

    没有。

    或者,从老套的意义上说是的,它对编译器生成的所有代码都很重要,即不允许编译器引入原始源代码中不存在的数据竞争(原则上,前面的措辞是可以在 C++11 内存模型中找到,但实际上支持线程的 C++98 编译器无论如何都会遵循它,否则就不可能创建健壮的线程程序)。

    IOW,编译器可以帮助您创建线程安全代码,使其不会使您编写的线程安全代码成为线程不安全的。但是,情况并非如此。没有神奇的线程安全精灵粉,编译器可以洒在你的线程不安全代码上以使其成为线程安全的。

    另一种解释是专门写的。运营商 new 从评论中复制粘贴“n.m”:

    MyClass::operator new 只是一个语法有些奇怪的常规函数​​。它与任何 ::operator 新变体无关,也不会神奇地继承它们的线程安全性

    或来自“Kerrek SB”的相同内容:

    就像 n.m.上面,我不听你的问题——重载的运算符只是一个完全普通的(静态)成员函数。它和你做的一样好或一样坏!如果你的 operator 内部的内存获取使用 ::operator new(n);,那么那个特定的调用是线程安全的,你也必须使其他一切都是线程安全的

    说实话,我很难弄清楚这里仍然不清楚的地方..

    【讨论】:

      【解决方案3】:

      是的。

      但请注意,在 C++11 中,new 是线程安全的。

      当然,当添加线程不安全代码时,它会使你的operator new线程不安全。

      根据您的编辑(改变了整个问题):

      编译器在新调用周围添加线程安全代码的假设是非常错误的。健全的实现将始终在 operator new 的内部实现中添加线程安全性(已经因为效率考虑,如每线程内存池)。

      也就是说,当你编写一个线程不安全的分配函数时,仅仅将它命名为operator new 并不会神奇地使它成为线程安全的,因为这就像任何其他函数一样,只是有一种特殊的调用方式。

      【讨论】:

      • 我没有得到你的答案。 C++03是否重载 operator new线程安全?
      • @iammilind:见前两个词。它是。或者换句话说:那里没有任何固有的线程不安全。但是由于您重载它,因此您在其中编写代码。该代码可以做一些不是线程安全的事情,这会使你的整个 operator new 不是线程安全的。但只要你只做线程安全的事情,它就会保持线程安全。
      • 喜欢 n.m.上面,我不听你的问题——重载的运算符只是一个完全普通的(静态)成员函数。它和你做的一样好或一样坏!如果您的操作员内部的内存获取使用::operator new(n);,那么该特定调用是线程安全的,您也必须使其他所有内容都是线程安全的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-21
      相关资源
      最近更新 更多