views:

2154

answers:

5

Is the following safe?

I am new to threading and I want to delegate a time consuming process to a separate thread in my C++ program. Using the boost libraries I have written code something like this:

thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);

Where finished_flag is a boolean member of my class. When the thread is finished it sets the value and the main loop of my program checks for a change in that value. I assume that this is okay because I only ever start one thread, and that thread is the only thing that changes the value (except for when it is initialised before I start the thread) So is this okay, or am I missing something, and need to use locks and mutexes, etc

+10  A: 

You never mentioned the type of finished_flag...

If it's a straight bool, then it might work, but it's certainly bad practice, for several reasons. First, some compilers will cache the reads of the *finished_flag* variable, since the compiler doesn't always pick up the fact that it's being written to by another thread. You can get around this by declaring the bool volatile, but that's taking us in the wrong direction. Even if reads and writes are happening as you'd expect, there's nothing to stop the OS scheduler from interleaving the two threads half way through a read / write. That might not be such a problem here where you have one read and one write op in separate threads, but it's a good idea to start as you mean to carry on.

If, on the other hand it's a thread-safe type, like a CEvent in MFC (or equivilent in boost) then you should be fine. This is the best approach: use thread-safe synchronization objects for inter-thread communication, even for simple flags.

Thomi
I won't worry so much about what the compiler caches value as the actual caches (T1, T2, and T3) on the processor. The reason is that in a multi-proc machine caches for each processor may not match up. That's one of the reasons you use the volatile keyword.
Yeah, maybe, but please: repeat after me:"volatile bool" is NOT a substitute for proper synchronisation.
Thomi
+6  A: 

Instead of using a member variable to signal that the thread is done, why not use a condition? You are already are using the boost libraries, and condition is part of the thread library.

Check it out. It allows the worker thread to 'signal' that is has finished, and the main thread can check during execution if the condition has been signaled and then do whatever it needs to do with the completed work. There are examples in the link.

As a general case I would neve make the assumption that a resource will only be modified by the thread. You might know what it is for, however someone else might not - causing no ends of grief as the main thread thinks that the work is done and tries to access data that is not correct! It might even delete it while the worker thread is still using it, and causing the app to crash. Using a condition will help this.

Looking at the thread documentation, you could also call thread.timed_join in the main thread. timed_join will wait for a specified amount for the thread to 'join' (join means that the thread has finsihed)

roo
+2  A: 

If you really want to get into the details of communication between threads via shared memory, even declaring a variable volatile won't be enough, even if the compiler does use appropriate access semantics to ensure that it won't get a stale version of data after checking the flag. The CPU can issue reads and writes out of order as long (x86 usually doesn't, but PPC definitely does) and there is nothing in C++9x that allows the compiler to generate code to order memory accesses appropriately.

Herb Sutter's Effective Concurrency series has an extremely in depth look at how the C++ world intersects the multicore/multiprocessor world.

MSN

Mat Noguchi
+2  A: 

Having the thread set a flag (or signal an event) before it exits is a race condition. The thread has not necessarily returned to the OS yet, and may still be executing.

For example, consider a program that loads a dynamic library (pseudocode):

lib = loadLibrary("someLibrary");
fun = getFunction("someFunction");
fun();
unloadLibrary(lib);

And let's suppose that this library uses your thread:

void someFunction() {
    volatile bool finished_flag = false;
    thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);
    while(!finished_flag) { // ignore the polling loop, it's besides the point
        sleep();
    }
    delete thrd;
}

void myclass::mymethod() {
    // do stuff
    finished_flag = true;
}

When myclass::mymethod() sets finished_flag to true, myclass::mymethod() hasn't returned yet. At the very least, it still has to execute a "return" instruction of some sort (if not much more: destructors, exception handler management, etc.). If the thread executing myclass::mymethod() gets pre-empted before that point, someFunction() will return to the calling program, and the calling program will unload the library. When the thread executing myclass::mymethod() gets scheduled to run again, the address containing the "return" instruction is no longer valid, and the program crashes.

The solution would be for someFunction() to call thrd->join() before returning. This would ensure that the thread has returned to the OS and is no longer executing.

bk1e
+4  A: 

Hi, I don't mean to be presumptive, but it seems like the purpose of your finished_flag variable is to pause the main thread (at some point) until the thread thrd has completed.

The easiest way to do this is to use boost::thread::join

// launch the thread...
thrd = new boost::thread(boost::bind(&myclass::mymethod, this, &finished_flag);

// ... do other things maybe ... 

// wait for the thread to complete
thrd.join();
ceretullis