Я разрабатываю Visual Studio 2008 в стандартном (неуправляемом) C++ под Windows XP Pro SP 3.
Я создал потокобезопасную оболочку вокруг std::cout. Этот объект-оболочка является заменой (то есть с тем же именем) того, что используется в качестве макроса, который был #defined для cout. Он используется в множестве кода. Его поведение, вероятно, в значительной степени соответствует вашим ожиданиям:
При построении он создает критическую секцию.
Во время вызова operator‹‹() он блокирует критическую секцию, передает данные для печати в cout и, наконец, освобождает критическую секцию.
При разрушении разрушает критическую секцию.
Эта оболочка находится в статическом хранилище (глобально). Как и все подобные объекты, он создается до запуска функции main() и разрушается после выхода из функции main().
Использование моей оболочки в деструкторе другого объекта, который также находится в статическом хранилище, проблематично. Так как порядок строительства/уничтожения таких объектов не определен, я вполне могу попытаться заблокировать критическую секцию, которая была уничтожена. Симптом, который я вижу, заключается в том, что моя программа блокируется при попытке блокировки (хотя я полагаю, что может произойти что угодно).
Что касается способов борьбы с этим...
Я ничего не мог сделать в деструкторе; в частности, я бы позволил критической секции продолжать жить. Стандарт C++ гарантирует, что cout никогда не умрет во время выполнения программы, и это будет наилучшей попыткой заставить мою оболочку вести себя аналогичным образом. Конечно, моя обертка будет «официально» мертва после запуска ее пустого деструктора, но, вероятно, (я ненавижу это слово) она будет так же функциональна, как и до запуска ее деструктора. На моей платформе это действительно так. Но, черт возьми, это уродливо, непереносимо и может сломаться в будущем...
Я держу критическую секцию (но не ссылку потока на cout) в файле pimpl. Всем обращениям к критическим секциям через pimpl предшествует проверка на ненулевое значение pimpl. Так получилось, что я забыл установить pimpl в 0 после вызова удаления в деструкторе. Если бы я установил это значение равным 0 (что я должен сделать в любом случае), вызовы моей оболочки после ее уничтожения ничего не сделают с критической секцией, но все равно будут передавать данные для печати в cout. На моей платформе это тоже работает. Опять некрасиво...
Я мог бы сказать своим товарищам по команде, чтобы они не использовали мою оболочку после выхода из main(). К сожалению, аэродинамика у этого была бы примерно такая же, как у танка.
ВОПРОСЫ:
* Вопрос 1 * Для случая 1, если я оставлю критическую секцию неповрежденной, произойдет утечка ресурсов критической секции в ОС. Будет ли эта утечка сохраняться после полного выхода из моей программы? Если нет, случай 1 становится более жизнеспособным.
* Вопрос 2 * Что касается случаев 1 и 2, кто-нибудь знает, могу ли я на моей конкретной платформе действительно безопасно продолжать использовать свою оболочку после запуска ее пустого деструктора? Похоже, я могу, но я хочу узнать, знает ли кто-нибудь что-нибудь определенное о том, как моя платформа ведет себя в этом случае...
*Вопрос 3* Мои предложения явно несовершенны, но действительно правильного решения я не вижу. Кто-нибудь знает правильное решение этой проблемы?
Примечание: конечно, может возникнуть обратная проблема, если я попытаюсь использовать свою оболочку в конструкторе другого объекта, который также находится в статическом хранилище. В этом случае я могу попытаться заблокировать еще не созданный критический раздел. Я хотел бы использовать идиому «построение при первом использовании», чтобы исправить это, но это влечет за собой синтаксическое изменение всего кода, который использует мою оболочку. Это потребовало бы отказа от естественности использования оператора ‹‹. И в любом случае слишком много кода, который нужно изменить. Так что это не рабочий вариант. Я не очень далеко продвинулся в мыслительном процессе по этой половине проблемы, но я задам один вопрос, который может быть частью другого несовершенного способа решения проблемы...
* Вопрос 4 * Как я уже сказал, моя обертка живет в статическом хранилище (оно глобальное) и у нее есть прыщ (гормональная проблема :) ). У меня сложилось впечатление, что необработанные байты переменной в статическом хранилище устанавливаются равными 0 во время загрузки (если они не инициализированы по-другому в коде). Это будет означать, что pimpl моей оболочки имеет значение 0 до создания моей оболочки. Это правильно?
Спасибо, Дэйв