views:

298

answers:

8

I would like to do something like the below for a multi-threaded program:

// wait for variable to become true but don't hog resources  
// then re-sync queues  

Is something like this a good solution?

while (!ready) {
    Thread.Sleep(250); // pause for 1/4 second;
};
+4  A: 

Try to use Event (kernel object) instead of simple variable and replace your loop by:

WaitForSingleObject(hEventHandle, INFINITE);
Andrey Shvydky
I think its meaning is much clearer if you use INFINITE instead of 0xffffffff.
Alexander Gessler
Yes, you are right.
Andrey Shvydky
+1  A: 

The code above will work, and maybe appropriate in some circumstances.

You could also look at a critical section or semaphore - this will make your application block and wait until the resource becomes available,

Your thread that does the work grabs the mutex, does some work, meanwhile, the main method also tries to grab the same mutex, but can't. when the worker thread(s) exit, they release the mutex and your main thread can pass the critical section and continue.

phatmanace
+11  A: 
Nikolai N Fetissov
Thank you for info about Conditional variables. This is new for me.
Andrey Shvydky
Third: Time spent sleeping could potentially be used doing something useful. Sleep() sucks.
John Dibling
Note that condition variables are new with Vista in case compatibility with XP is important.
Michael Burr
Boost comes with a very nice implementation of condition variables which works on older version of Windows as well.
jalf
@John Dibling, sleeping in a thread (in general) does not suck, it will allow other threads to do something useful. It is not a busy loop wasting CPU time.
Trent
@Trent It sucks because it could have waited only 100ms instead of 250ms. If the waiting code is done 10 times in a loop, you'll wait 2.5 seconds instead of maybe 1 second. It's silly things like that which lead to sluggish user interfaces and slow network communications. By using proper synchronization techniques, you get the least amount of latency waiting for something to complete.
Emile Cormier
@Emile: I think Trent's point is that the Sleep function *in general* does not suck. It is certainly the wrong tool in this case, but that doesn't mean it should never be used.
jalf
@jalf: Gotcha. Sorry Trent.
Emile Cormier
A: 

First of all, you need to declare your 'ready' variable at least 'volatile' or this could have nasty side effects. Secondly, sleeping that long vefore reevaluating the condition is only a good idea if the duration it might take is indeed very long, let's say a few minutes.

Using the WinAPI's Event functions (CreateEvent, SetEvent(), WaitForSingleEvent()) is the best way to do it. Of course it introduces some overhead, but usually it's fine.

If you want to stick with your solution, looping and rechecking the condition a few times before you sleep again could improve performance in some scenarios.

Alexander Gessler
A: 

Granted this is C#, but I've found this book to be extremely helpful for doing multi-threading development.

http://www.albahari.com/threading/

Some of the info is not language specific.

cmw
A: 

The raw Win32 API has EVENT for doing this, here's a usage example:

http://msdn.microsoft.com/en-us/library/ms686915(VS.85).aspx

However, that API is C-oriented and particular to Windows. If writing a C++ program you might consider making your code more platform independent by using something like boost::threads, which has an analogue in Conditions.

A caveat I've found is that Windows can WaitForMultipleObjects, thus waiting on several events (and other handle classes) at a time. boost has no parallel AFAIK.

Hostile Fork
You can use the same Boost condition_variable for checking multiple variables, as long as the variables are protected by the same mutex.
Emile Cormier
Just added an answer that shows how to use boost condvar for multiple "events".
Emile Cormier
Yup that's possible! But the issue I speak of relates to the fact that Windows's wait is unusually diverse. (e.g. if you wait on a handle to a thread and the semantics are that it waits for the thread to terminate, you can wait on sockets...) Not an issue using boost for thread events until you try and port some code which waits on multiple *types* of event source in a single operation (!)
Hostile Fork
A: 

On top of good answers already provided - you will waste half the sleep time, assuming a random distribution of the occurrence you wish to detect. 125ms is an eternity in computer time.

WaitForSingleObject on a Win32 Event handle allows you to detect the required signal pseudo-immediately (depending on what other threads in your process are doing), and not do redundant checks (how many needless loops do you have to execute before the signal arrives?), provided the setting thread call SetEvent once it's done with its work. The bool is then redundant, which is as it should be.

Steve Townsend
+2  A: 

Here's how you do it using boost:

boost::condition_variable condvar;
boost::mutex mutex;
bool finished1 = false;
bool finished2 = false;

void longComputation1()
{
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished1 = false;
    }
    // Perform long computation
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished1 = true;
    }
    condvar.notify_one();
}

void longComputation2()
{
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished2 = false;
    }
    // Perform long computation
    {
        boost::lock_guard<boost::mutex> lock(mutex);
        finished2 = true;
    }
    condvar.notify_one();
}

void somefunction()
{
    // Wait for long computations to finish without "spinning"
    boost::lock_guard<boost::mutex> lock(mutex);
    while(!finished1 && !finished2)
    {
        condvar.wait(lock);
    }

    // Computations are finished
}

For the sake of brevity, I didn't include the thread spawning code.

The boost::lock_guard uses the RAII idiom to automatically unlock the mutex when the lock object goes out of scope. Very useful for preventing deadlocks in case of exceptions.

I find condition variables less error prone than Microsoft's Event objects. If you use boost.Thread, you'll have the added benefit of cross-platform potability.

Emile Cormier
I knew this was possible--see my comment on my answer--but +1'ing you since you're trying to shed light on something other than slavish dedication to error-prone C and the Microsoft-lock-in Windows API. Baffles me why people upvote answers that don't challenge that, but, guess we're a minority here.
Hostile Fork
I posted it for the benefit of others not familiar with boost condvars, and (like you said) to show there's almost always a portable alternative to Windows-only API. :)
Emile Cormier