2009-08-26 6 views
9

Для реализации класса new_handler я столкнулся с следующим примером в книге «эффективный C++». Это выглядит проблемой в многопоточной среде, My Question - как достичь класса new_handler в многопоточной среде?с использованием специфичного класса set_new_handler

void * X::operator new(size_t size) 
{ 
    new_handler globalHandler =    // install X's 
    std::set_new_handler(currentHandler); // handler 
    void *memory; 
    try {          // attempt 
     memory = ::operator new(size);   // allocation 
    } 
    catch (std::bad_alloc&) {     // restore 
     std::set_new_handler(globalHandler);  // handler; 
     throw;         // propagate 
    }           // exception 
    std::set_new_handler(globalHandler);  // restore 
               // handler 
    return memory; 
} 

ответ

4

Вы правы. Вероятно, это не безопасно для потоков. Вы можете рассмотреть альтернативный подход, как с помощью nothrow version of new вместо:

void* X::operator new(std::size_t sz) { 
    void *p; 
    while ((p = ::operator new(sz, std::nothrow) == NULL) { 
    X::new_handler(); 
    } 
    return p; 
} 

Это заставит ваш обработчик класса конкретных будет вызываться всякий раз выделение памяти. Я бы не сделал этого, пока вы не поймете все головные боли, связанные с перегрузкой operator new. В частности, прочитайте две части статьи Херба Саттера: К Новому, Перчатку, Чтобы Бросить, Part 1 и Part 2. Интересно, что он говорит, чтобы избежать версии nothrow ... хммм.

0

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

0

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

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

Это решение должно хорошо работать в однопоточной среде, но будет работать в многопоточной среде. Чтобы заставить его работать в многопоточной среде, необходимо, чтобы вызывающий абонент уведомил объект обработчика о том, что он работает в определенном потоке; передача идентификатора потока с классом будет хорошим способом сделать это. Если обработчик вызывается, то он проверяет идентификатор потока и определяет поведение для выполнения на основе связанного класса. Когда вызов new() завершен, просто отмените идентификатор потока, чтобы обеспечить правильное поведение по умолчанию (как вы уже делаете при сбросе обработчика по умолчанию).