Все, что статически инициализировано или лениво инициализировано (например, статическая переменная области блока, чей содержащий блок был введен), деинициализируется во время обычного завершения программы - либо путем возврата main()
, либо после вызова exit()
.
Проблема не столько в последовательности завершения потоков, сколько в , что вообще не делается попыток остановить их .Вместо этого, пожинающие (возможно, все еще работающие) потоки делегируются операционной системе для сортировки, когда процесс завершается.
Практически говоря, для реализации было бы действительно трудно принудительно завершить потоки - отсоединеноили иным образом.Помимо всего прочего, это рецепт непредсказуемого поведения, поскольку эти потоки почти всегда блокируются объектами синхронизации или системными вызовами и удерживают ресурсы (привет взаимоблокировки!).Во-вторых, Posix-Threads не предоставляет API для этого.Не удивительно, что потоки должны вернуться из своей функции потока, чтобы выйти.
Существует конечный промежуток времени между возвратом main()
и завершением процесса, в котором среда выполнения выполняет статическую деинициализацию (в строгом порядке, обратном последовательности инициализации), а также все, что зарегистрировано с помощью atexit()
, в течение которого все еще может выполняться любой существующий поток.В большой программе это время может быть значительным.
Если какой-либо из этих потоков получает доступ к статически инициализированному объекту, это, конечно, неопределенное поведение .
Недавно я потратил немало времени, чтобы отследить серию сбоев в большом приложении для iOS с большим количеством C ++.
Код сбоев выглядел примерно так, с падением глубоко в недрахиз std::set<T>::find(const T&)
bool checkWord(const std::string &w)
{
static std::set<std::string> tags{"foo", "bar"};
return (tags.find(w) != tags.end());
}
Между тем, в главном потоке был вызов exit()
нескольких функций в стеке.
Приложения для iOS и macOS сильно многопоточныеиспользуя Grand Central Dispatch / libdispatch, и оказывается, что потоки не только продолжают работать после выхода main()
, но и выполняются задания из фоновых очередей отправки.
Я подозреваю, что подобная ситуация будетмногие другие системы.
Я не нашел ужасно хорошего решения этой проблемы, кроме как избежать статических вычислений в блоке в пользу данных, которые не требуют инициализации.