我有一个单例,初始化起来很昂贵:
struct X {...};
const X&
get_X()
{
static const X x = init_X();
return x;
}
第一次get_X()
调用时,可能需要花费数百毫秒的时间来初始化函数本地静态变量。但是完成之后,我需要做的事情X
相对较快:
get_X().find_something_for_me(); // expensive if this is the first call
get_X().find_something_for_me(); // now fast
如何最大程度地减少首次致电时的较大延迟get_X()
?我有很多核心...
一旦您的应用程序启动,并且(有希望)在您实际需要调用之前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
破坏,但OS线程继续运行,get_X
直到完成。
当thread
标准化时,有人认为detach
edthread
不仅无用,而且很危险。但是,这里是detach
edthread
的一个非常安全且颇具启发性的用例。
如果我的应用程序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不实现线程安全的函数局部静态变量。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句