运行昂贵的初始化的最佳方法是什么?

How*_*ant 13 c++ c++11

我有一个初始化成本很高的单例:

struct X {...};

const X&
get_X()
{
    static const X x = init_X();
    return x;
}
Run Code Online (Sandbox Code Playgroud)

第一次get_X()调用时,初始化函数本地静态可能需要几百毫秒.但在那之后,我需要做的事情X相对较快:

get_X().find_something_for_me();  // expensive if this is the first call
get_X().find_something_for_me();  // now fast
Run Code Online (Sandbox Code Playgroud)

我怎样才能在第一次打电话时最大限度地减少延迟get_X()?我有很多核心......

How*_*ant 20

一旦您的应用程序启动,并且(希望)在您实际需要呼叫之前get_X(),请无偿地呼叫它.此外,为了使您的初始化阶段更快,请将昂贵的初始化提供给不同的线程.例如:

#include <thread>

int
main()
{
    std::thread(get_X).detach();
    // continue with other initialization...
}
Run Code Online (Sandbox Code Playgroud)

当某个任务与几百毫秒(或更多)一样昂贵时,旋转线程来处理它的开销是在噪声级别.如果您使用的是多核硬件(现在还不是什么?),那么如果您的应用程序在get_X完成初始调用之前实际上不需要任何内容​​,那么这将是一个明显的性能获胜.

备注/问题:

  • 为什么detachthread?为什么不join呢?

    如果您决定join这样做thread,那意味着您只需等待它完成,为什么不做其他事情.完成detach后,请自行清理.你甚至不需要保留一个句柄thread.临时std::thread破坏,但OS线程继续存在,运行get_X完成.

    什么thread时候被标准化,有观点认为detach编辑thread不仅没用,而且很危险.但这里是绝对安全的,而且相当激励用例detachED thread秒.

  • 如果我的应用程序get_X()detached thread完成第一次调用之前调用该get_X()怎么办?

    性能受到打击,但不是正确性.您的应用程序将阻止此行get_X():

    static const X x = init_X();

    直到detached thread完成执行.因此,没有数据竞争.

  • 如果我的申请在detached thread完成之前结束怎么办?

    如果您的应用程序在初始化阶段结束,那么显然会出现灾难性错误.如果get_X接触at_exit链已经被破坏的东西(在主要之后执行),就会发生坏事.但是,您已经处于恐慌关闭的状态......再一次紧急情况不太可能使您的恐慌情况更加严重.你已经死了 OTOH,如果你的初始化是什么,需要几分钟到几个小时,你可能需要约当你的初始化完成后更好的沟通,更正常关机程序.在这种情况下,您需要在您的线程中实现合作取消,分离或不分离(std委员会拒绝为您提供).

  • 如果第一次调用get_X()抛出异常怎么办?

    在这种情况下,第二次调用get_X()有机会初始化函数local static,假设你没有保留未被捕获的异常并允许它终止你的程序.它可能会抛出,或者它可能在初始化中成功(这取决于您的代码).在任何情况下,调用get_X()将继续尝试初始化,等待初始化正在进行,直到某人设法这样做而不抛出异常.无论呼叫是否来自不同的线程,这都是正确的.

综上所述

std::thread(get_X).detach();
Run Code Online (Sandbox Code Playgroud)

这是一种利用多核功能在不影响线程安全正确性的情况下尽快获得独立昂贵的初始化的好方法.

唯一的缺点是,get_X()无论您是否需要,都可以初始化数据.所以在使用这种技术之前一定要确保你需要它.


[脚注]对于那些使用Visual Studio的人来说,这是转向VS-2015的良好动力.在此版本之前,VS不实现线程安全的函数 - 局部静态.

  • 再加上一个,因为我学到了新的东西.那么在遇到这个问题之后的某个时候我可能会遇到它.但是,很高兴知道.:) (2认同)