Situation:
Some thread A has processed an incoming trigger and wants to inform a waiting thread B to continue with his part of the work.
Schematic solution
Both threads have access to some specific shared data, a corresponding mutex and the condition variable (e.g. the variables may be class attributes and the threads are working with the same class instance):
#include <mutex>
#include <condition_variable>
SomeSpecificDataStructure mySpecificData;
std::mutex myDataMutex;
std::condition_variable myCondVar;
Basics: with separate checking and waiting
Thread A finishes his part of the work and informs the waiting thread B to start his action:
{
...
{
std::lock_guard<std::mutex> guard(myDataMutex);
mySpecificData.SomeField = ..
}
myCondVar.notify_one();
...
}
If data conditions are not yet fulfilled thread B waits until he receives the signal to recheck conditions:
bool DataAreReadyForProcessing ()
{
return true/false;
}
{
...
std::unique_lock<std::mutex> uLock(myDataMutex);
while(!DataAreReadyForProcessing())
{
myCondVar.wait(uLock);
}
mySpecificData.SomeField = ..
Pay attention to the following points within the code snippet:
- if the data conditions are already fulfilled then you may not call wait() on the condition variable (otherwise you may be blocked forever)
- you have to lock the mutex and pass it (within uLock) to function wait()
- wait() will automatically unlock the mutex when the thread is set to wait state (otherwise thread A would not be able to change your specific data)
- when wait() returns the mutex is automatically relocked. You can directly recheck the conditions of your data and also change the data
For more info see condition_variable - complete reference at CppReference.com
Preferable: Embed checking into wait()
There is also a specialized wait function which allows you to specify the checking code as a predicate. Using the boolean function from above you could simplify the code. The explicitly programmed while loop will disappear (the loop is executed within wait()):
{
...
std::unique_lock<std::mutex> uLock(myDataMutex);
myCondVar.wait(uLock, DataAreReadyForProcessing);
mySpecificData.SomeField = ..
}
As an alternative to a boolean function you could also pass a function object or a lambda expression:
myCondVar.wait(uLock, []{return mySpecificData.readyToProcess;});
Time limited waiting
To avoid indefinite blocking of a thread when a condition does not come true you could specify a maximum wait time. The wait will return either when the condition is fulfilled or when the timeout has elapsed. It is your task to analyze the reason why wait() has returned.
When using the wait() function without predicate you get a return value std::cv_status::timeout or std::cv_status::no_timeout:
std::chrono::seconds timeoutPeriod = 5;
auto timePoint = std::chrono::system_clock::now() + timeoutPeriod;
std::unique_lock<std::mutex> uLock(myDataMutex);
while(!DataAreReadyForProcessing())
{
if (myCondVar.wait_until(uLock, timePoint)
== std::cv_status::timeout)
{
break;
}
}
When using the wait() function with predicate you get a boolean return value (false means "timeout has elapsed"):
if (myCondVar.wait_for(uLock, timeoutPeriod,
DataAreReadyForProcessing))
{
}
else
{
}
Tip:
Always prefer passing a predicate to the wait() functions. Then your code will stay more simple.