views:

107

answers:

4
#include <iostream>
#include <boost/thread.hpp>
using std::endl; using std::cout;
using namespace boost;


mutex running_mutex;

struct dostuff
{
    volatile bool running;
    dostuff() : running(true) {}
    void operator()(int x)
    {
        cout << "dostuff beginning " << x << endl;
        this_thread::sleep(posix_time::seconds(2));
        cout << "dostuff is done doing stuff" << endl;
        mutex::scoped_lock running_lock(running_mutex);
        running = false;
    }
};

bool is_running(dostuff& doer)
{
    mutex::scoped_lock running_lock(running_mutex);
    return doer.running;
}

int main()
{
    cout << "Begin.." << endl;
    dostuff doer;
    thread t(doer, 4);

    if (is_running(doer)) cout << "Cool, it's running.\n";

    this_thread::sleep(posix_time::seconds(3));

    if (!is_running(doer)) cout << "Cool, it's done now.\n";
    else cout << "still running? why\n"; // This happens! :(

    return 0;
}

Why is the output of the above program:

Begin..
Cool, it's running.
dostuff beginning 4
dostuff is done doing stuff
still running? why

How can dostuff correctly flag when it is done? I do not want to sit around waiting for it, I just want to be notified when it's done.

+2  A: 

You can't assume the thread will be finished just by sleeping.

You can call join on the thread. This will wait until the thread is done and then resume flow.

For advanced notifying between threads of a certain event happening you can use boost condition.

Brian R. Bondy
How is join a solution? Note: "I do not want to sit around waiting for it"...
Kyle
@Kyle: That's only part of my answer.
Brian R. Bondy
A: 

The real question isn't how the dostuff thread should send the signal, but rather how the main thread should receive the signal. My favorite method is to use socketpair() to create a local socket connection and then give one socket to the child thread and the other socket to the main thread. The two threads can then use the socket-connection to communicate with each other. In your case, all you would need is for the child thread to send a byte on the socket (or just close its socket file descriptor) just before it exits, and that would be enough to break the main thread out of select() or poll() or whatever it is blocking in and let it know that the child thread has finished its task.

Note that the main thread should still call join() on the child thread's thread-ID (after it receives the child-going-away signal), to make sure that the child thread is really really dead, before freeing any resources... otherwise you risk a race condition of the main thread freeing a resource after the child thread has signalled but before the thread-cleanup routines have completed.

Jeremy Friesner
A: 

I'm guessing your problem is actually a bug in your code. From the Boost documentation for thread:

Thread Constructor with arguments

template <class F,class A1,class A2,...>
thread(F f,A1 a1,A2 a2,...);

Preconditions:
F and each An must by copyable or movable.

Effects:
As if thread(boost::bind(f,a1,a2,...)). Consequently, f and each an are copied into internal storage for access by the new thread.

So, I think the thread is modifying its own copy of doer, and not the object whose runnable state you're checking.

Owen S.
+4  A: 

The problem in this example is that there are two instances of dostuff, so the version being set to false in operator() is different then the one in main.

From the thread management documentation:

A new thread is launched by passing an object of a callable type that can be invoked with no parameters to the constructor. The object is then copied into internal storage, and invoked on the newly-created thread of execution. If the object must not (or cannot) be copied, then boost::ref can be used to pass in a reference to the function object. In this case, the user of Boost.Thread must ensure that the referred-to object outlives the newly-created thread of execution.

If you don't want to copy the object, use boost::ref:

thread t(boost::ref(doer), 4);
R Samuel Klatchko
perfect, thank you
Kyle