Initializing global object from one thread and waiting for it in other threads Is everything OK with this code? 

I need this mechanism: 


I want to initialize global object of type A (named 'gA'), which may take very long time
I don't want to wait for gA to be initialized, I want to start other threads at the same time (in order for the program to be responsive)
Other threads also take some time to initialize their own stuff, and when they are ready, they want to use the gA. It can happen at any moment, before or after gA is initialized. I must assume that other threads are ready before gA is initialized and must wait therefore wait for gA.


I came up with this simple solution using shared_future. The code works and does what I want. I only wonder if there are some dangers hidden within this approach. Maybe you can spot some bug in this code? 

#include &lt;iostream&gt;
#include &lt;chrono&gt;
#include &lt;memory&gt;
#include &lt;future&gt;
#include &lt;functional&gt;

using namespace std;
using namespace chrono_literals;

struct A
{
    A() { cout &lt;&lt; "ctor A" &lt;&lt; endl; }
    ~A() { cout &lt;&lt; "dtor A" &lt;&lt; endl; }
    void talk(string name) { cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] A is talking from thread " &lt;&lt; name &lt;&lt; endl; }
};

// global A object which threads of type B want to use
unique_ptr&lt;A&gt; gA;

bool initA()
{ 
    int i = 0;
    while (i++ &lt; 7)
    {
        this_thread::sleep_for(1s);
        cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] Initializing global object gA, it takes a lot of time... (" &lt;&lt; i &lt;&lt; ")" &lt;&lt; endl;
    }
    gA.reset(new A());
    cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] gA is ready to use!" &lt;&lt; endl;
    return true;
};

void funB(string name, shared_future&lt;bool&gt; futA)
{
    int i = 0;
    while (i++ &lt; 3)
    {
        this_thread::sleep_for(1s);
        cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] Initializing thread " &lt;&lt; name &lt;&lt; "... (" &lt;&lt; i &lt;&lt; ")" &lt;&lt; endl;
    }
    cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] Now thread " &lt;&lt; name &lt;&lt; " waits to use global gA" &lt;&lt; endl;
    futA.wait();

    cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] Ok, now thread " &lt;&lt; name &lt;&lt; " can use global gA" &lt;&lt; endl;
    gA-&gt;talk(name);
}

int main()
{
    cout &lt;&lt; "[" &lt;&lt; this_thread::get_id() &lt;&lt; "] Starting main..." &lt;&lt; endl;

    shared_future&lt;bool&gt; sharedFutA(async(launch::async, initA));

    thread tB1(funB, string("B1"), sharedFutA);
    thread tB2(funB, string("B2"), sharedFutA);
    thread tB3(funB, string("B3"), sharedFutA);

    tB1.join();
    tB2.join();
    tB3.join();

    cout &lt;&lt; "Done" &lt;&lt; endl;
}
Seems reasonable to me. Just a few nits first:


You should never using namespace std; in real code. Just write std::string, and so on. It'll save your readers some confusion, and it'll avoid subtle bugs related to ADL that you'll only learn about much later in life.
using namespace std::chrono_literals is presumably a type for using namespace std::literals or maybe using namespace std::literals::chrono_literals.
You've got some spurious semicolons: }; should be } in at least one place.




Unless the value of the bool being passed through the future — true or false — is meaningful, it shouldn't be part of your program. In this case you could (and therefore should) be using std::shared_future&lt;void&gt; instead of std::shared_future&lt;bool&gt;.



There is a simpler and therefore perhaps better thread-synchronization mechanism, if all your threads are launching from the same spot at the same time. Use a mutex which is locked when the threads spawn, and force each thread to "check in" by locking that mutex before it's allowed to continue. For example:

// global A object which threads of type B want to use
std::unique_ptr&lt;A&gt; gA;

void initA(std::unique_lock&lt;std::mutex&gt; lk)
{ 
    // do setup here

    gA = std::make_unique&lt;A&gt;();

    // now signal that it's okay for the B threads to start
    lk.unlock();
}

void funB(std::string name, std::mutex&amp; checkpoint)
{
    // do initial setup here

    checkpoint.lock(); checkpoint.unlock();

    gA-&gt;talk(name);
}

int main() {
    std::mutex checkpoint;
    std::unique_lock&lt;std::mutex&gt; lk(checkpoint);
    auto tB1 = std::thread(funB, string("B1"), std::ref(checkpoint));
    auto tB2 = std::thread(funB, string("B2"), std::ref(checkpoint));
    auto tB3 = std::thread(funB, string("B3"), std::ref(checkpoint));
    initA(std::move(lk));
[...]


Notice that the lk.unlock() in initA isn't strictly needed, as written, because the destructor of lk was going to drop that lock anyway. But you could put more code after lk.unlock() if you had more things to do that didn't affect gA.



Also notice that I'm using make_unique instead of reset(new ...). Always prefer to use make_unique and make_shared instead of raw new and delete — again because it helps avoid subtle exception-related bugs which if you're lucky you'll never see in the wild; but also because if you get in the habit of never writing new or delete yourself, you'll be able to spot more easily when other people's code incorrectly uses new or delete. Ideally, you want to get in a situation where you can install a git commit hook that does a git grep \bnew\b and fails if it finds any!