Безопасность потоков для перегруженного оператора new - PullRequest
4 голосов
/ 12 октября 2011

Хотя стандарт не гарантирует поточную безопасность для new, большинство многопоточных операционных систем поддерживают поточную безопасность operator new.

Я реализую свое собственное управление памятью для динамического выделения определенных class (скажем, MyClass) в моем коде. Для обеспечения безопасности потоков MyClass мне может понадобиться библиотека pthread или boost::.

Я думал, что если 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;
}

Я предполагаю, что поскольку global ::new является поточно-ориентированным, этот перегруженный operator new также должен быть поточно-ориентированным. Другими словами, компилятор должен генерировать код, связанный с безопасностью потоков, везде, где встречается ключевое слово new. Это правильное предположение?

Ответы [ 3 ]

3 голосов
/ 12 октября 2011

Это так.

Однако обратите внимание, что в C ++ 11 new является поточно-ориентированным.

Конечно, когда вы добавляете небезопасный код потока, это делает ваш поток operator new небезопасным.

После вашего редактирования (которое изменило весь вопрос):

Предположение, что компилятор добавляет код безопасности потока вокруг , новый вызов довольно неправильный.Разумные реализации всегда будут добавлять безопасность потоков во внутреннюю реализацию оператора new (уже из соображений эффективности, таких как пулы памяти для каждого потока).

То есть, когда вы пишете функцию небезопасного выделения потока, просто присвоив ей имя operator new не сделает его поточно безопасным, поскольку это подобно любой другой функции, только с особым способом вызова..

2 голосов
/ 12 октября 2011

Я думаю, что с вашим отредактированным вопросом, ответ определенным "Нет".

Вы также, кажется, смущены новыми операторами и новыми выражениями (нет такой вещи, как ::new(), есть только ::new и ::operator new()), поэтому, возможно, лучше разбить это на части.

Когда вы пишете T * p = new T;, где T имеет перегруженный оператор-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
}
0 голосов
/ 12 октября 2011

Из обновленного вопроса:

Я предполагаю, что поскольку global :: new является поточно-ориентированным, этот перегруженный оператор new также должен быть поточно-ориентированным. Другими словами, компилятор должен генерировать код, связанный с безопасностью потоков, везде, где встречается новое ключевое слово. Это правильное предположение?

Нет.

Или, да, в том банальном смысле, что это важно для всего кода, который генерирует компилятор, а именно, что компилятору не разрешено вводить гонки данных, которых не было в исходном исходном коде (в принципе, предыдущая формулировка находится в модели памяти C ++ 11, но на практике компиляторы C ++ 98, поддерживающие потоки, в любом случае следуют ей, потому что в противном случае было бы невозможно создать надежные многопоточные программы).

IOW, компилятор помогает вам создавать потокобезопасный код до такой степени, что он не сделает поточно-ориентированный код, который вы пишете, потокобезопасным. Однако, это не идет другим путем; нет волшебной нити для безопасности потоков, которую компилятор может посыпать на ваш небезопасный код, чтобы сделать его потокобезопасным.

Другое объяснение, в частности, относительно оператор новый скопирован из комментария "n.m":

MyClass :: operator new - это обычная функция с несколько странным синтаксисом. Он не относится ни к какому новому варианту :: operator и не наследует их потокобезопасность.

или то же самое из "Kerrek SB":

Как н.м. выше я не слежу за вашим вопросом - перегруженный оператор - это просто обычная (статическая) функция-член. Это так хорошо или так плохо, как вы делаете это! Если для получения памяти в вашем операторе используется :: operator new (n);, то этот конкретный вызов является поточно-ориентированным, и вы должны также сделать все остальное поточно-ориентированным.

По правде говоря, у меня проблемы с выяснением того, что здесь до сих пор неясно ..

...