Предположим, я запускаю a, std::thread
а затем detach()
it, поэтому поток продолжает выполняться, даже если тот, std::thread
который когда-то представлял его, выходит из области видимости.
Предположим далее, что программа не имеет надежного протокола для присоединения к отсоединенному потоку 1 , поэтому отсоединенный поток по-прежнему работает при main()
выходе.
Я не могу найти ничего в стандарте (точнее, в проекте N3797 C ++ 14), который описывает, что должно произойти, ни 1.10, ни 30.3 не содержат соответствующих формулировок.
1 Другой, возможно, эквивалентный вопрос: «можно ли когда-нибудь снова присоединить отсоединенный поток», потому что к какому бы протоколу вы ни придумали присоединиться, сигнальная часть должна была быть выполнена, пока поток еще работал, и планировщик ОС мог бы решите перевести поток в спящий режим на час сразу после того, как была выполнена сигнализация, и принимающая сторона не сможет надежно определить, что поток действительно завершен.
Если завершение работы main()
с отсоединенными потоками является неопределенным поведением, тогда любое использование std::thread::detach()
является неопределенным поведением, если только основной поток никогда не завершает работу 2 .
Таким образом, завершение работы main()
с отсоединенными потоками должно иметь определенные эффекты. Вопрос в том, где (в стандарте C ++ , а не в POSIX, не в документации по ОС, ...) определяются эти эффекты.
2 Отдельная нить не может быть соединена (в смысле std::thread::join()
). Вы можете ждать результатов от отсоединенных потоков (например, через будущее std::packaged_task
или с помощью счетного семафора или флага и переменной условия), но это не гарантирует, что поток завершил выполнение . В самом деле, если не поставить сигнализацию части в деструктор первого автоматического объекта потока, там будет , вообще говоря , код (деструкторы) , которые выполняются после кода сигнализации. Если ОС планирует, что основной поток получит результат и завершит работу до того, как отдельный поток завершит работу с указанными деструкторами, что будет определяться ^ W?
std::exit
или выходом изmain
него достаточно, но не обязательно, чтобы удовлетворить эти требования». (весь абзац может быть релевантным) Также см. [support.start.term] / 8 (std::exit
вызывается, когдаmain
возвращается)Ответы:
Ответ на оригинальный вопрос «что происходит с отдельным потоком при
main()
выходе»:Он продолжает работать (потому что стандарт не говорит, что он остановлен), и это четко определено, если он не затрагивает (автоматический | thread_local) переменные других потоков или статические объекты.
Похоже, это позволяет разрешать диспетчеры потоков как статические объекты (об этом говорится в [basic.start.term] / 4 , спасибо @dyp за указатель).
Проблемы возникают, когда уничтожение статических объектов завершено, потому что тогда выполнение входит в режим, в котором может выполняться только код, разрешенный в обработчиках сигналов ( [basic.start.term] / 1, 1-е предложение ). Из стандартной библиотеки C ++ это только
<atomic>
библиотека ( [support.runtime] / 9, 2-е предложение ). В частности, это - в общем случае - исключаетcondition_variable
(это определяется реализацией, сохраняется ли это для использования в обработчике сигнала, потому что оно не является частью<atomic>
).Пока вы не размотали свой стек на этом этапе, трудно понять, как избежать неопределенного поведения.
Ответ на второй вопрос «могут ли отдельные потоки когда-либо присоединяться снова»:
Да, с
*_at_thread_exit
семейством функций (notify_all_at_thread_exit()
,std::promise::set_value_at_thread_exit()
, ...).Как отмечено в сноске [2] вопроса, сигнализации переменной состояния, семафора или атомного счетчика недостаточно для присоединения к отдельному потоку (в смысле обеспечения того, что конец его выполнения произошел - до получения указанная сигнализация ожидающим потоком), потому что, как правило, будет больше кода, выполняемого, например, после
notify_all()
переменной состояния, в частности деструкторов автоматических и локальных для потока объектов.Запуск сигнализации как последнее, что делает поток ( после того, как произошли деструкторы автоматических и локальных для потока объектов ) - это то, для чего
_at_thread_exit
было разработано семейство функций.Таким образом, чтобы избежать неопределенного поведения при отсутствии каких-либо гарантий реализации, превышающих требования стандарта, необходимо (вручную) присоединить отдельный поток с
_at_thread_exit
функцией, выполняющей сигнализацию, или заставить отдельный поток выполнять только код, который был бы безопасен для обработчик сигналов тоже.источник
exit
завершения уничтожения статических объектов, запускаatexit
обработчиков, очистки потоков и т. Д. Он возвращает управление среде хоста, то есть процесс завершается. Если отсоединенный поток все еще работает (и каким-то образом избежал неопределенного поведения, не касаясь чего-либо за пределами своего собственного потока), он просто исчезает в клубе дыма при выходе из процесса.main
вызовыpthread_exit
вместо возврата или вызоваexit
, это заставит процесс ждать завершения отдельных потоков, а затем вызоветexit
после завершения последнего из них.Отсоединение темы
По словам
std::thread::detach
:От
pthread_detach
:Отключение потоков в основном для сохранения ресурсов, если приложению не нужно ждать завершения потока (например, демоны, которые должны работать до завершения процесса):
std::thread
объекту выйти из области видимости, не присоединяясь, что обычно приводит к вызовуstd::terminate()
уничтожения.Убийства темы
Поведение при завершении процесса такое же, как и у основного потока, который может хотя бы перехватить некоторые сигналы. То, могут ли другие потоки обрабатывать сигналы, не так важно, так как можно присоединиться или завершить другие потоки в вызове обработчика сигналов основного потока. (Связанный вопрос )
Как уже говорилось, любой поток, независимо от того, отсоединен он или нет, умрет с его процессом в большинстве операционных систем . Сам процесс может быть остановлен путем подачи сигнала, вызова
exit()
или возврата из основной функции. Тем не менее, C ++ 11 не может и не пытается определить точное поведение базовой ОС, тогда как разработчики Java VM могут в некоторой степени абстрагировать такие различия. AFAIK, экзотические модели процессов и потоков обычно встречаются на древних платформах (на которые C ++ 11, вероятно, не будет перенесен) и различных встроенных системах, которые могут иметь специальную и / или ограниченную реализацию языковой библиотеки, а также ограниченную языковую поддержку.Поддержка потоков
Если потоки не поддерживаются, они
std::thread::get_id()
должны возвращать недопустимый идентификатор (по умолчаниюstd::thread::id
создан), поскольку существует простой процесс, которому не нужен объект потока для выполнения, и конструктор объектаstd::thread
должен выдать astd::system_error
. Вот как я понимаю C ++ 11 в сочетании с современными ОС. Если есть ОС с поддержкой потоков, которая не порождает основной поток в своих процессах, дайте мне знать.Управление потоками
Если для правильного завершения необходимо сохранить контроль над потоком, это можно сделать с помощью примитивов синхронизации и / или каких-либо флагов. Однако в этом случае я предпочитаю устанавливать флаг завершения работы с последующим соединением, поскольку нет смысла увеличивать сложность путем отсоединения потоков, поскольку ресурсы все равно будут освобождены одновременно, где несколько байтов
std::thread
объекта по сравнению с более высокой сложностью и, возможно, больше синхронизирующих примитивов должно быть приемлемым.источник
Рассмотрим следующий код:
При запуске в системе Linux сообщение из thread_fn никогда не печатается. ОС действительно очищается,
thread_fn()
как толькоmain()
выходит. Заменаt1.detach()
сt1.join()
всегда печатает сообщение , как ожидалось.источник
Судьба потока после выхода из программы - неопределенное поведение. Но современная операционная система очистит все потоки, созданные процессом при его закрытии.
При отсоединении
std::thread
эти три условия будут продолжать выполняться:*this
больше не владеет какой-нитьjoinable()
всегда будет равноfalse
get_id()
будет равноstd::thread::id()
источник
detach()
неопределенного поведения? Трудно поверить ...Когда основной поток (то есть поток, который выполняет функцию main ()) завершается, тогда процесс завершается, и все другие потоки останавливаются.
Ссылка: https://stackoverflow.com/a/4667273/2194843
источник
Чтобы другие потоки могли продолжить выполнение, основной поток должен завершиться вызовом pthread_exit (), а не exit (3). Хорошо использовать pthread_exit в main. Когда используется pthread_exit, основной поток прекращает выполнение и остается в состоянии зомби (перестал существовать) до тех пор, пока все остальные потоки не завершатся. Если вы используете pthread_exit в основном потоке, не можете получить статус возврата других потоков и не можете выполнить очистку для других потоков (это можно сделать с помощью pthread_join (3)). Кроме того, лучше отсоединять потоки (pthread_detach (3)), чтобы ресурсы потока автоматически высвобождались при завершении потока. Общие ресурсы не будут освобождены, пока не завершатся все потоки.
источник