重载操作员新的线程安全

时间:2011-10-12 11:11:00

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

虽然标准不保证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关键字的任何地方发出与线程安全相关的代码。这是正确的假设吗?

3 个答案:

答案 0 :(得分:3)

是的。

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

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

编辑后(改变了整个问题):

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

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

答案 1 :(得分:2)

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

您似乎也对新运算符和新表达式感到困惑(没有::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中的函数调用才是最重要的。这个调用是否是线程安全的?那么,这完全取决于如何定义该功能!标准中有 nothing 可以保证该函数的任何特定行为,这毕竟是一个完全普通的函数。

(新)标准或编译器供应商唯一保证的是默认提供的全局函数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 :(得分:0)

来自更新的问题:

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

没有。

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

IOW,编译器可以帮助您创建线程安全的代码,使其不会使您编写的线程安全的代码不安全。但是,它没有反过来;没有神奇的线程安全小精灵灰尘,编译器可以在你的线程不安全的代码上撒上它以使其线程安全。

具体说明另一种解释。运算符new通过“n.m”复制粘贴:

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

或“Kerrek SB”中的相同内容:

比如上午上面,我没有按照您的问题 - 重载运算符只是一个完全普通的(静态)成员函数。这与你做的一样好或坏!如果运算符内部的内存获取使用:: operator new(n);,则该特定调用是线程安全的,并且您必须使其他所有内容都是线程安全的

说实话,我很难搞清楚这里还不清楚的事情......