2
votes

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.

3
What makes you think std::thread is reusable ? - Sid S
@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
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
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
@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

3 Answers

1
votes

From std::thread::operator = ()

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

1
votes

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.

1
votes

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!