Я пишу многопоточное приложение c ++ для * nix операционных систем. Каковы некоторые рекомендации для изящного завершения такого приложения? Мой инстинкт заключается в том, что я хочу установить обработчик сигнала на SIGINT (SIGTERM?), Который останавливает / присоединяет мои потоки. Кроме того, возможно ли «гарантировать», что все деструкторы вызываются (при условии, что при обработке сигнала не выдается никаких других ошибок или исключений)?
Некоторые соображения приходят на ум:
назначьте 1 поток, который будет отвечать за организацию выключения, например, как предположил Dithermaster, это может быть основным потоком, если вы пишете отдельное приложение. Или, если вы пишете библиотеку, предоставьте интерфейс (например, вызов функции), посредством которого клиентская программа может завершать объекты, созданные в библиотеке.
Вы не можете гарантировать, что деструкторы вызваны; это зависит от вас, и требует тщательного вызова удаления для каждого нового. Может быть, умные указатели помогут вам. Но, действительно, это соображение дизайна. Основные компоненты должны иметь начало & остановите семантику, которую вы можете выбрать из конструктора класса & деструктор.
последовательность выключения для набора взаимодействующих объектов — это то, что может потребовать некоторых усилий, чтобы получить корректность. Например, перед удалением объекта вы уверены, что какой-нибудь механизм таймера не попытается вызвать его через несколько микро / милли / секунд спустя? Метод проб и ошибок — ваш друг здесь; разработать рамки, которые могут многократно & быстро запускать и останавливать приложение, чтобы выявить связанные с отключением условия гонки.
сигналы являются одним из способов вызвать событие; другие могут периодически опрашивать известный файл или открывать сокет и получать по нему некоторые данные. В любом случае вы хотите отделить код последовательности выключения от события триггера.
Я рекомендую, чтобы основной поток закрыл все рабочие потоки перед выходом из себя. Отправьте каждому работнику событие, в котором говорите, что он должен очиститься и выйти, и подождите, пока каждый из них сделает это. Это позволит всем деструкторам C ++ работать.
Относительно управления сигналами, единственное, что ты можешь переносимый а также безопасно сделать внутри обработчика сигнала, чтобы записать в переменную типа sig_atomic_t
(возможно, volatile
Квалифицированный) и возврат. В общем ты не могу вызывать большинство функций и не должен написать в глобальную память. Другими словами, обработчик должен просто установить флаг для проверки внутри вашей основной подпрограммы, в какой-то момент вы посчитаете нужным, и оттуда должно быть выполнено действие, вытекающее из самого сигнала.
(Поскольку это может быть связано с блокировкой ввода / вывода, подумайте POSIX Нить отмены. Ваш клон Unix (особенно Linux) может иметь особенности в отношении этого и выше.)
Что касается деструкторов, магия не участвует. Они будут выполнены, если управление покинет заданную область с помощью любых средств, определенных в языке. Оставив область видимости другими способами (например, longjmp()
или даже exit()
) не вызывает деструкторов.
Относительно общих методов выключения, Есть разные мнения по этому вопросу.
Некоторые утверждают, что должно быть выполнено «изящное завершение» в смысле освобождения каждого когда-либо выделенного ресурса. В C ++ это обычно означает, что все деструкторы должны быть правильно выполнены до завершения процесса. Это сложно на практике и часто вызывает много горя, особенно в многопоточных программах, по разным причинам. Сигналы еще больше усложняют саму природу асинхронной диспетчеризации сигналов.
Поскольку большая часть этой работы абсолютно бесполезна, некоторые другие, такие как я, утверждают, что программа должна просто завершиться немедленно, возможно, вскоре после отмены постоянных изменений в системе (например, удаления временных файлов или восстановления разрешения экрана) и сохранения конфигурации. Более аккуратная очистка — не только пустая трата времени (поскольку операционная система будет очищать большинство вещей, таких как выделенная память, висячие потоки и дескрипторы открытых файлов), но может быть серьезный пустая трата времени (освобождающие средства могут касаться выгружаемой памяти, бесполезно вынуждая систему вызывать их на странице, например, просто для того, чтобы освободить их вскоре после завершения процесса), не говоря уже о возможности возникновения взаимоблокировок из присоединяющихся потоков.
Просто сказать нет. Когда вы хотите уйти, позвоните exit()
(или даже _exit()
, но остерегайтесь незапущенного ввода / вывода) и все. Больше раздражают, чем медленно запускающие программы, медленно завершающие программы.