используя специфичный для класса set_new_handler - PullRequest
9 голосов
/ 26 августа 2009

Для специфичной для класса реализации new_handler я наткнулся на следующий пример в книге «Эффективный c ++». Это выглядит проблемой в многопоточной среде. Мой вопрос: как добиться специфичного для класса 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;
}

Ответы [ 3 ]

4 голосов
/ 26 августа 2009

Ты прав. Это вероятно не потокобезопасный. Возможно, вы захотите рассмотреть альтернативный подход, такой как использование nothrow версии 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. В частности, прочитайте статью Херба Саттера, состоящую из двух частей: To New, Perchance To Throw, Часть 1 и Часть 2 Интересно, что он говорит, чтобы избежать версии nothrow ... хммм.

0 голосов
/ 26 августа 2009

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

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

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

0 голосов
/ 26 августа 2009

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

...