3

I seem to be getting different thread object assignment behaviour between boost and std threads. If I use boost threads, a thread member variable can be reassigned and the thread recreated. If I use std threads, I get a runtime error terminate called without an active exception.

Here is the code in question (run, then replace std:: with boost::)

class ThreadTest 
 {
    private:
    std::thread mythread;
    std::atomic<bool> running_;

    int doSomeWork(){
        int i=0;
        cout << "starting" << endl;
        while(running_){
            cout << "working" << endl;
            std::this_thread::sleep_for (std::chrono::seconds(1));
            if (i>3){ break; } else { i++; }
        }
        running_ = false;
    }

    public:
    void runThread(){
        running_ = true;
        mythread = std::thread(&ThreadTest::doSomeWork, this);
    }

    void joinThread(){
        mythread.join();
    }
 };

int main(){ 
    ThreadTest test;
    test.runThread();
    std::this_thread::sleep_for (std::chrono::seconds(10));
    test.runThread();
    test.joinThread();
    return 0;
 }

Output for boost::

starting
working
working
working
working
working
starting
working
working
working
working
working

Output for std::

starting
working
working
working
working
working
terminate called without an active exception
Aborted (core dumped)

This particular piece of code is used in a library which doesn't seem to have boost as a dependency. I would like to keep it that way, so is there a way to get the boost 'reassignment' behaviour using std threads?

EDIT - SOLUTION

I added an std::atomic<bool> threadInitialized_; to the class which is set to true in the thread function doSomeWork(). My runThread() method becomes:

void runThread(){
    if(threadInitialized_)
       mythread.join();

    running_ = true;
    mythread = std::thread(&ThreadTest::doSomeWork, this);
}

I am aware this will block the main thread until the spawned thread is done.

aleksk
  • 661
  • 7
  • 15
  • What makes you think `std::thread` is reusable ? – Sid S Nov 29 '18 at 04:39
  • @SidS I would like to know if there is a way to reuse it, and if not, what the std::thread equivalent way to do what I'm trying to do – aleksk Nov 29 '18 at 04:44
  • 1
    Since boost 1.50 `boost::thread` and `std::thread` should behave the same in this respect. There's a preprocessor macro you can define to revert to the old behavior, but it isn't the default. – Miles Budnek Nov 29 '18 at 04:52
  • Apart from the technical issues, there another thing that will bite you: You seem to have unbalanced calls to runThread() and joinThread(), and you expect that to work! This is an indicator to me that your code mixes responsibilities. If this class is responsible for maintaining a background service, give it the full responsibility for doing that. Ignore startup requests when the services is running and ignore shutdown requests if it isn't. In between, the necessary restart of a thread should remain the responsibility of this class, nobody else's. – Ulrich Eckhardt Nov 29 '18 at 06:26
  • @UlrichEckhardt the joinThread() only exists for the purpose of this example, otherwise my main function would return and I get no output. Thanks for your input though, maybe it will come in handy for someone else! In my real code, I check whether the thread is already running and ignore the request if it is. – aleksk Nov 29 '18 at 06:29

3 Answers3

2

Typically, as correctly pointed out above, all (joinable) threads need to be joined or detached before their object is destroyed.

Now, one (of many) differences between boost threads and std::thread is that Boost threads detach themselves inside their destructor which std::thread's do not; your incorrect usage of std::thread therefore correctly fires terminate().

PS: Do NOT (!!) believe the other commentors above that std::threads and boost::thread should behave "the same" - this is just not true!

Skriptkiddie
  • 411
  • 2
  • 7
  • "Boost threads detach themselves inside their destructor" - this features is now deprecated, see https://www.boost.org/doc/libs/1_66_0/doc/html/thread/thread_management.html#thread.thread_management.thread.destructor. – Daniel Langr Nov 29 '18 at 06:23
  • 1
    Please, provide the evidence. The linked documentation says the exact opposite. (Quote: _The reason to moving to `std::terminate` is that either implicitly detaching or joining a `joinable()` thread in its destructor could result in difficult to debug correctness (for `detach`) or performance (for `join`) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. Join the thread before destroying or use a scoped thread._). – Daniel Langr Nov 29 '18 at 06:33
1

From std::thread::operator = ()

"If [the thread object] is joinable, terminate() is called."

Sid S
  • 6,037
  • 2
  • 18
  • 24
  • I see, so is there a way to do what I'm trying to do other than using some kind of thread pool? – aleksk Nov 29 '18 at 04:50
  • 1
    You have to either stop or detach the thread before you reuse the `std::thread` object. – Miles Budnek Nov 29 '18 at 04:51
  • 1
    You could make the thread function wait for some sort of signal to do more work, instead of returning. – Sid S Nov 29 '18 at 04:51
  • OK thanks for the ideas - I think I need to recreate the thread however, because it needs to restart a socket connection. Thread termination occurs when bad data is received or socket is terminated in some manner – aleksk Nov 29 '18 at 04:58
1

While using boost::thread, if you don't explicitly call join() or detach() then the boost::thread destructor and assignment operator will call detach() on the thread object being destroyed/assigned to respectively. With a C++11 std::thread object, this will result in a call to std::terminate() and abort the application. In this case you have to call detach() or join() manually.

rakesh.sahu
  • 465
  • 8
  • 18