What is the best way to wait for a variable in a multithreaded application

10,277

Solution 1

No, this is not a good solution. First it might sleep too long. Second it's easy for threads to get into lockstep. Here's couple of links to MSDN articles on proper synchronization techniques:

Solution 2

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.

Solution 3

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

WaitForSingleObject(hEventHandle, INFINITE);

Solution 4

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.

Share:
10,277

Related videos on Youtube

T.T.T.
Author by

T.T.T.

Updated on April 15, 2022

Comments

  • T.T.T.
    T.T.T. about 2 years

    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;
    };
    
  • Andrii Shvydkyi
    Andrii Shvydkyi over 14 years
    Thank you for info about Conditional variables. This is new for me.
  • John Dibling
    John Dibling over 14 years
    Third: Time spent sleeping could potentially be used doing something useful. Sleep() sucks.
  • Michael Burr
    Michael Burr over 14 years
    Note that condition variables are new with Vista in case compatibility with XP is important.
  • josesuero
    josesuero over 14 years
    Boost comes with a very nice implementation of condition variables which works on older version of Windows as well.
  • Emile Cormier
    Emile Cormier over 14 years
    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
    Emile Cormier over 14 years
    Just added an answer that shows how to use boost condvar for multiple "events".
  • Trent
    Trent over 14 years
    @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.
  • Emile Cormier
    Emile Cormier over 14 years
    @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.
  • josesuero
    josesuero over 14 years
    @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.
  • HostileFork says dont trust SE
    HostileFork says dont trust SE over 14 years
    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 (!)
  • HostileFork says dont trust SE
    HostileFork says dont trust SE over 14 years
    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.
  • Emile Cormier
    Emile Cormier over 14 years
    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. :)
  • underscore_d
    underscore_d almost 8 years
    It's important to note that all of the boost things demonstrated here have been accepted into the stdlib and thus are now completely standard, making them even more superior to proprietary ways of doing the same stuff.