Ответ 1
Как только ваше приложение запустится, и (надеюсь), прежде чем вам действительно нужно позвонить get_X()
, безвозмездно вызовите его. Кроме того, чтобы ваша стадия инициализации была быстрее, подайте свои дорогостоящие инициализации на разные потоки. Например:
#include <thread>
int
main()
{
std::thread(get_X).detach();
// continue with other initialization...
}
Когда какая-то задача стоит так дорого, как несколько сотен миллисекунд (или больше), накладные расходы на отключение потока для борьбы с ним - это уровень шума. И если вы работаете на многоядерном оборудовании (что не в наши дни?), То это четкий выигрыш в производительности, если ваше приложение действительно ничего не нуждается в этом синглтоне, пока не завершится первоначальный вызов get_X
.
Примечания/Вопросы:
-
Почему
detach
thread
? Почему бы неjoin
?Если вы решите
join
этотthread
, это означает, что вам просто нужно подождать, пока он закончит, а почему бы и нет. Когда он закончит,detach
очистится после себя. Вам даже не нужно сохранять дескрипторthread
. Временныйstd::thread
разрушает, но поток ОС продолжает работать, завершаяget_X
.Когда стандартизован
thread
, были точки зрения, чтоdetach
edthread
были не только бесполезны, но и опасны. Но вот совершенно безопасный и вполне мотивирующий прецедент дляdetach
edthread
s. -
Что делать, если мое приложение вызывает
get_X()
до того, какdetach
edthread
завершит первый вызовget_X()
?Появляется удар производительности, но не попадание правильности. Ваше приложение будет блокироваться в этой строке в
get_X()
:static const X x = init_X();
пока
detach
edthread
не закончит выполнение. Таким образом, нет расы данных. -
Что делать, если мое приложение заканчивается до завершения
detach
edthread
?Если ваше приложение заканчивается на этапе инициализации, что-то, очевидно, катастрофически ошибочно. Если
get_X
касается чего-то, что уже разрушено цепочкойat_exit
(которая выполняется после main), произойдут плохие вещи. Тем не менее, вы уже находитесь в состоянии панического отключения... еще одна аварийная ситуация вряд ли ухудшит вашу панику. Ты уже мертв. Otoh, если ваша инициализация - это что-то, что занимает от нескольких минут до нескольких часов, вам, вероятно, нужно улучшить общение о завершении инициализации и более грациозных процедурах закрытия. В этом случае вам необходимо осуществить совместное аннулирование в ваших потоках, отключенных или нет (то, что комитет std отказался предоставить вам). -
Что делать, если первый вызов
get_X()
вызывает исключение?В этом случае второй вызов
get_X()
имеет возможность инициализировать функцию local static, предполагая, что вы не оставите исключение неотображаемым и разрешите ему завершить работу вашей программы. Он может слишком бросаться, или может преуспеть в инициализации (это зависит от вашего кода). В любом случае вызовыget_X()
будут продолжать пытаться инициализировать, ожидая выполнения инициализации, пока кому-то не удастся сделать это, не выбрасывая исключение. И это все правда, поступают ли вызовы из разных потоков или нет.
В заключение
std::thread(get_X).detach();
- это хороший способ использовать мощь ваших нескольких ядер, чтобы как можно быстрее получить независимые дорогостоящие инициализации, без ущерба для правильной точности потока.
Единственным недостатком является то, что вы инициализируете данные в get_X()
, нужен ли вам это или нет. Поэтому убедитесь, что вам это понадобится, прежде чем использовать эту технику.
[Сноска] Для тех, кто использует Visual Studio, это хорошая мотивация для перехода на VS-2015. До этой версии VS не реализует потокобезопасную функцию - локальную статику.