2

Here is a quick example:

class worker
{
    std::thread thread1;
    std::thread thread2;

    worker(){}

    start()
    {
        thread1 = std::thread([]()
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(50000));
        });
        thread1.deteach();

        thread2 = std::thread([]()
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(50000));
        });
        thread2.deteach();
    }

    ~worker()
    {
        // Ignore this part! - as per Richard's comment :)
        //thread1.join();   // <-- do I need this at all?
        //thread2.join();   // <-- do I need this at all?
    }
}

int main()
{
    worker *p_worker = new worker();
    p_worker->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 1 sec

    delete p_worker;
}
  • Create the worker
  • Start the threads which last for 50 seconds
  • After 1 second delete the worker (calls destructor)
  • In worker destructor I re-join the threads (probably should check if they are joinable first?) - not really sure I need to do this.
  • Then worker is destroyed

I had a look at this question: how-do-i-terminate-a-thread-in-c11 which suggests there is no c11 portable way to terminate the threads.

Questions:

  • Are the threads destroyed completely (no dregs/leaks left)?
    • If "yes" do I need to re-join the threads in order for them to be destroyed? EDIT - Richard pointed out this is N/A
  • Is this a sensible approach?
code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • 6
    You can't join a detached thread – Richard Critten Apr 11 '18 at 09:35
  • @RichardCritten ah, right - I'll update the Q with that in mind, thanks : ) – code_fodder Apr 11 '18 at 09:36
  • 3
    Generally if a class "owns" threads you don't detach them, signal them in some way to terminate in the destructor and join them. – Matteo Italia Apr 11 '18 at 09:40
  • 1
    "Is this a sensible approach" to what? Your threads are just sleeping at the moment so that's fine; but when you come to doing something with them, if the threads are using any global resources then it's probably not fine. If your threads are doing something CPU intensive; then it's probably not fine either since you want them to stop rather than work for 50 seconds on something that can't be used. – UKMonkey Apr 11 '18 at 09:41
  • @MatteoItalia mm.. good point - I was convinced I needed to detach them to allow main to carry on. But I think I got that confused with not joining them, thanks : ) – code_fodder Apr 11 '18 at 09:51
  • @UKMonkey I think your comment answers what I needed to know. In my real code I was using a mutex to "signal" when the thread is idle - so I would have been happy to just destroy worker... – code_fodder Apr 11 '18 at 09:52
  • @RichardCritten please add as a partial answer - i'll mark it up. – code_fodder Apr 11 '18 at 09:53
  • @MatteoItalia please add as a partial answer - i'll mark it up. – code_fodder Apr 11 '18 at 09:53
  • @UKMonkey please add as a partial answer - i'll mark it up. – code_fodder Apr 11 '18 at 09:53

2 Answers2

4

As already stated in comments, you cannot join a detached thread. Detached threads are meant to run independently. In general, it is a bad idea to detach a thread owned by a class.

I would suggest using a boolean to control the lifecycle of your thread.

For example, you could do something like this:

class worker
{
private:
    std::thread thread1;
    std::atomic<bool> thread1ShouldRun;
    std::thread thread2;
    std::atomic<bool> thread2ShouldRun;

    void workerFunc1() {
        bool threadWorkIsDone = false;
        while (thread1ShouldRun.load()) {
            // Do Stuff
            // Set threadXShouldRun to false when you're done
            // thread1ShouldRun.store(false);
        }
    }

    void workerFunc2() {
        bool threadWorkIsDone = false;
        while (thread2ShouldRun.load()) {
            // Do Stuff
            // Set threadXShouldRun to false when you're done
            // thread2ShouldRun.store(false);
        }
    }

public:
    worker() {}

    void start()
    {
        thread1ShouldRun.store(true);
        thread1 = std::thread(&worker::workerFunc1, this);
        thread2ShouldRun.store(true);
        thread2 = std::thread(&worker::workerFunc2, this);            
    }

    ~worker()
    {
        thread1ShouldRun.store(false);
        // Protection in case you create a worker that you delete and never call start()
        if (thread1.joinable())
            thread1.join();
        thread2ShouldRun.store(false);
        if (thread2.joinable())
            thread2.join();
    }
};

int main()
{
    worker *p_worker = new worker();
    p_worker->start();
    std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // 1 sec

    delete p_worker; // Threads will be joined here
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Clonk
  • 2,025
  • 1
  • 11
  • 26
3

Yes, the threads are completely destroyed by the std::thread dtor if they are still joinable (running and not detached).

That's not good news though, as std::terminate() will be called, killing the whole process.

In general, just terminating is only sensible to avoid further damage from an unexpected state, or if the application was built for harmless termination at that exact point.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • oh...bugger! - so if std::terminate() called, from what I understand (admittedly not-so-much!), then the whole application will terminate... as you say, not good news :( – code_fodder Apr 11 '18 at 09:59
  • This is all correct information, but should probably be clarified a bit for OP; the "if they are still joinable" bit means "if they are not detached and still running". So, this all means that *if you don't join a non-detached thread, your program will be killed"; the correct course of action here is to join before `std::thread` gets destroyed. – Matteo Italia Apr 11 '18 at 10:03
  • @MatteoItalia So if the threads are joined in worker destructor (like I had it originally - but without being detached) - then, this approach may work? - i.e. std::terminate() won't be called? – code_fodder Apr 11 '18 at 10:10
  • 1
    Yes. Detaching is meant to launch independent threads, free to run by themselves (and to possibly join them through other means). If you have an owner of the thread, generally you want to join on its destruction. – Matteo Italia Apr 11 '18 at 10:14