0

I have an object with a normal constructor. The constructor has a lot of initialization to do. Much of this initialization can be performed asynchronously, so I am calling a new thread from within the constructor.

When the thread in initialized on the stack, it appears that the thread is destroyed when the constructor exits which causes a crash. This would look like so:

class MyObject
{
    MyObject()
    {
        // Typical initialization
        // ...

        // Time consuming initialization
        std::thread(&MyObject::Init; this); // Create new thread to call Init();

        // Crash when exit MyObject() here
    }

    void Init()
    {
         // Time consuming operations
    }
};

The alternative (which works) is to create the thread on the heap as such.

class MyObject
{
    std::thread* StartupThread;

    MyObject()
    {
        // Typical initialization
        // ...

        // Time consuming initialization
        StartupThread = new std::thread(&MyObject::Init; this); // Create new thread to call Init();

        // Crash when exit MyObject() here
    }
    ~MyObject()
    {
        StartupThread->join();
        delete StartupThread;
    }

    void Init()
    {
         // Time consuming operations
    }

};

My Question

Is there any harm is leaving the unjoined & undisposed thread object alive for the lifetime of the object or should I try to clean it up as soon as Init() finishes?

Is there a way to "automatically" dispose the thread when it finishes so it isn't left hanging around?

Essentially, can I get the thread on the stack in some way without it crashing as I described?

OlivierLi
  • 2,798
  • 1
  • 23
  • 30
Russell Trahan
  • 783
  • 4
  • 34
  • It shouldn't _crash_; it should detach. Also note that this is not happening at the end of your constructor, but right after the declaration, since you don't give the `std::thread` a name (this is just a temporary in an unused expression). I think you should check the lifetime of `MyObject`. Unfortunately, you did not provide a minimal testcase so it is impossible to know the true cause. – Lightness Races in Orbit Jan 28 '15 at 19:48

2 Answers2

3

What about:

class MyObject
{
    MyObject ()
    {
        f = std::async (std::launch::async, &MyObject::Init, this);
    }

private:
    void Init ();
    std::future<void> f;
};

This allows you to do f.get () when you want to synchronize on the task. The destructor will join automatically if it is the last living copy of the object (you may want to delete the copy constructor if you don't want this behavior).

Note that you want to synchronize at some point before destruction, since if Init throws an exception, your destructor will.

Also, see this if you want to go the detach route.

Community
  • 1
  • 1
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
0

You can call std::thread::detach() from the stack, but this is very dangerous considering the object could be long deleted while it's still running.

According to your requirements, the best option is to make sure it joins at the end of both Init() and the de-constructor. I think the better design is to join() at the end of the constructor for simplicity. Assuming there's no other processing to be done once you start the thread and when Init() is invoked, this would be the best choice.

PDizzle745
  • 356
  • 1
  • 7
  • There can be many of these `MyObject` objects created and this is part of a real-time application. The `MyObject` is a dataset that can be used once it is finished being initialized. Not calling `Init()` asynchronously causes a huge hangup. `Init()` must be allowed to run longer than the constructor itself. – Russell Trahan Jan 28 '15 at 19:46
  • Understood. As Alexandre C and I stated, as long as you join the thread/future at the end of `Init()`and the de-constructor it should be okay. – PDizzle745 Jan 28 '15 at 19:48