Можно ли безопасно создавать потоки во время статической инициализации?

В какой-то момент я читал, что потоки нельзя безопасно создавать до первой строки main(), потому что компиляторы вставляют специальный код, чтобы потоки работали во время статической инициализации. Поэтому, если у вас есть глобальный объект, который создает поток при построении, ваша программа может рухнуть. Но сейчас я не могу найти исходную статью, и мне любопытно, насколько сильное это ограничение — строго ли оно соответствует стандарту? Верно ли это для большинства компиляторов? Останется ли это верным в C++0x? Может ли компилятор, соответствующий стандартам, сделать саму статическую инициализацию многопоточной? (например, обнаружение того, что два глобальных объекта не касаются друг друга, и их инициализация в отдельных потоках для ускорения запуска программы)

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


person Joseph Garvin    schedule 18.09.2009    source источник
comment
строго ли это соответствует стандарту - стандарт С++ 03 ничего не говорит о потоках. Места, где можно посмотреть, что касается текущего поведения, - это POSIX (который, конечно, является стандартом, просто не стандартом), MSDN, Boost или другой специфичный для реализации документы для используемого вами компилятора, платформы и потокового API.   -  person Steve Jessop    schedule 18.09.2009


Ответы (3)


То, о чем вы говорите, относится не только к языку, но и к библиотеке времени выполнения C (CRT).
Для начала, если вы создаете поток, используя собственный вызов, такой как CreateThread() в Windows, вы можете это сделать. в любом месте, потому что он переходит прямо в ОС без вмешательства CRT.
Другой вариант, который у вас обычно есть, — это использовать _beginthread(), который является частью CRT. Есть некоторые преимущества в использовании _beginthread(), такие как errno, безопасная для потоков. Подробнее об этом читайте здесь. Если вы собираетесь создавать потоки с помощью _beginthread(), могут возникнуть некоторые проблемы, поскольку инициализации, необходимые для _beginthread(), может не быть на месте.

Это затрагивает более общий вопрос о том, что именно происходит перед main() и в каком порядке. По сути, у вас есть функция точки входа в программу, которая заботится обо всем, что должно произойти до main() с Visual Studio, вы действительно можете посмотреть на этот фрагмент кода, который находится в CRT, и выяснить для себя, что именно там происходит. Самый простой способ добраться до этого кода — остановить точку останова в вашем коде и просмотреть кадры стека до main().

person shoosh    schedule 18.09.2009
comment
Спасибо, это дает мне некоторое представление о том, как обстоят дела в Windows с MSVC. Однако мне все еще любопытны другие платформы, и на самом деле это не дает ответа, безопасно ли это в Windows или нет (действительно ли _beginthread() полагается на какую-либо инициализацию, которая, возможно, еще не произошла?). - person Joseph Garvin; 18.09.2009
comment
Хотел бы я знать это также. Документы, кажется, не упоминают об этом. - person shoosh; 18.09.2009

Основной проблемой является ограничение Windows на то, что вы можете и не можете делать в DllMain. В частности, вы не должны создавать потоки в DllMain. Статическая инициализация часто происходит из DllMain. Отсюда логически следует, что нельзя создавать потоки во время статической инициализации.

person MSalters    schedule 21.09.2009
comment
Но обратите внимание: During process startup and DLL initialization routines, new threads can be created, but they do not begin execution until DLL initialization is done for the process с msdn.microsoft.com /en-us/library/ms682453%28v=VS.85%29.aspx - person Martin York; 17.07.2011
comment
Справедливое замечание, не видел этого. Обратите внимание, что комментарий относится конкретно к CreateThread, однако _beginthreadex не имеет этого исключения. - person MSalters; 18.07.2011

Насколько я могу судить из черновика C++0x/1x, запуск потока до main() — это нормально, но все же подвержен обычным ловушкам статической инициализации. Соответствующая реализация должна убедиться, что код для инициализации потоков выполняется до того, как это сделают какие-либо статические конструкторы или конструкторы потоков.

person coppro    schedule 22.09.2009