6

This is a continuation to my question: Thread creation inside constructor

Now that I have successfully created a thread in a constructor, I know that it must be joined. As I am developing an API, I have no control over the main function so I can't join it there.

Will it be right to join the thread in the class destructor, considering that the instanced object of that class would have an application's lifetime?

Community
  • 1
  • 1
V Shreyas
  • 449
  • 5
  • 19
  • Is there a need to join the thread ? If the destructor is called it mean the main program has finished its work. Threads need to be joined only when you cannot proceed further until everyone finishes execution. – unbesiegbar Jun 19 '15 at 06:45
  • Yes, I need the other thread to complete in case of any work, even if the main function is over. – V Shreyas Jun 19 '15 at 06:46
  • I consider the question too vague. If you are developing an API then it depends on what this API is abstracting and then you can decide accordingly. – marom Jun 19 '15 at 06:46
  • Hey, it seems you are creating only a single thread in a class. I think you are asking if a user creates multiple objects (inherently multiple threads) and you want to join all those ?. If so, then there is no need to join them. – unbesiegbar Jun 19 '15 at 06:51
  • If I create an object of the class anywhere in a project, and it is global, as soon as main ends, it will destroy the object, as well as the thread called by the constructor. I want that thread to finish. So, I was thinking of joining it in the destructor such that the destruction of the object will stop until the end of that thread. – V Shreyas Jun 19 '15 at 06:56
  • I am asking if it poses any problem. – V Shreyas Jun 19 '15 at 06:57

4 Answers4

8

You could do that. However, are you really interested in the mechanism of launching a new thread, or are you perhaps interested in the effect of performing something asynchronously? If it's the latter, you can move to the higher level async mechanism:

#include <iostream>
#include <future>


void bar()
{
    std::cout << "I'm bar" << std::endl;
}

class foo
{
public:
    foo() :
        m_f(std::async(
            std::launch::async,
            bar))
    {

    }

    ~foo()
    {
        m_f.get();
    }

private:
    std::future<void> m_f;
};

int main ()
{
    foo f;
}
  • You're asking in the constructor to launch bar asynchronously. You don't care about managing the thread yourself - let the library handle that.

  • Place the resulting future in a member.

  • In the dtor, get the future.

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
  • Brilliant! Can you please elaborate on the difference between the two, the "mechanism" and the "intent" – V Shreyas Jun 19 '15 at 07:01
  • 1
    @VShreyas The `thread` API is relatively low level. You create these objects, store them, and join them. The asynchronous API focuses instead on the tasks: You launch functions with a delcared intent of how they should run (asyncrhonously, deferred, combinations thereof), and let the library do the assignment to threads. – Ami Tavory Jun 19 '15 at 07:04
  • @AmiTavory, Very nice example.. :) Didn't knew of this. These days I am liking C++.. :D – unbesiegbar Jun 19 '15 at 07:08
  • I'd like to point out that currently this answer doesn't really answer the question *"Will it be right to join the thread in the class destructor, considering that the instanced object of that class would have an application's lifetime?"*... – hyde Jun 19 '15 at 08:11
  • @hyde The first sentence addresses that, no? The code written would work as well for launching a thread explicitly at the ctor and joining at the dtor. – Ami Tavory Jun 19 '15 at 08:15
  • haha. I am really glad that I am getting tremendous response for my queries on stackoverflow, even for queries that might be no-brainers for knowledge giants here. Thank you everyone. – V Shreyas Jun 19 '15 at 10:19
2

You have encountered the one of the shortcomings of C++ RAII: Destructors can't easily report errors, and a destructors can't fail gracefully, because they have no return value and throwing exceptions is bad idea.

So if the other thread is not responding to a request to stop, what can destructor do? It can wait more or violently destroy the other thread or leave it running, none of which are very nice options. And then it can either ignore the situation, just log it, or throw an exception despite the risk of application terminating immediately, again not a very nice set of options.

So, you have at least 3 options

  • The joined thread is known to be well-behaved, so you know it will terminate promptly when requested, and join in destructor is safe. The risk is, program will hang on the join if thread isn't terminating.
  • You determine that the error handling options (explained above) available in destructor are sufficient, and join in destructor is ok. You have to add the error detection and handling code to destructor (for example a timeout).
  • Provide a separate "close" method, which perhaps takes timeout argument, and then returns error value or throws exception on failure.

With threads, a thread failing to terminate promptly can often be considered a programming error comparable to a segfault, so you could choose to terminate the application with helpful (to the developer) diagnostic message (2nd bullet point above), or just let the program hang (1st bullet point above). But this is a bit risky, because if down the road you need to create a thread which can't terminate quickly (it is doing a blocking system call, or it must notify the other end of a network connection, which may be slow), and it's a good idea to think how to solve this before locking down a design.

Community
  • 1
  • 1
hyde
  • 60,639
  • 21
  • 115
  • 176
1

Sure, you could do. just make sure the thread will exist otherwise your program will hang forever on that join.

Johan Engblom
  • 215
  • 1
  • 12
1

I have come up with a threading pattern that I have found handles all of the weird situations in C++ regarding running threads in classes.

http://chrisd.nl/blog/how-to-run-threads/

Chris Desjardins
  • 2,631
  • 1
  • 23
  • 25