Waiting for one of two conditions
Situation
You are using two worker threads each doing a different piece of work. You want to react as soon as one of the threads has done its part of the work.
Solution concept
Both worker threads are using the same condition variable but they set different boolean flags within your specific data. The predicate within the wait function then can check for these flags and apply arbitrary boolean expressions to them, e.g. flag1 || flag2.
Data definitions shared by all threads:
#include <mutex>
#include <condition_variable>
SomeSpecificDataStructure mySpecificData;
std::mutex myDataMutex;
std::condition_variable myCondVar
Thread A finishes his part of the work and informs the waiting thread C:
{
...
{
std::lock_guard<std::mutex> guard(myDataMutex);
mySpecificData.threadAHasProcessedData = true;
}
myCondVar.notify_one();
...
}
In the same way thread B finishes his part of the work and informs the waiting thread C:
{
...
{
std::lock_guard<std::mutex> guard(myDataMutex);
mySpecificData.threadBHasProcessedData = true;
}
myCondVar.notify_one();
...
}
Thread C reacts as soon as one of the threads A or B has done its work:
{
...
std::unique_lock<std::mutex> uLock(myDataMutex);
myCondVar.wait(uLock, []{
return mySpecificData.threadAHasProcessedData
|| mySpecificData.threadBHasProcessedData;);
...
}
Waiting for both threads
By changing the boolean expression within the lambda expression you can easily change to waiting until both threads have done their work. Simply replace "||" with "&&".
WaitableCondition - simplified synchronization
Situation:
You only want to send a trigger from thread A to thread B to start its processing. You don't have any data structures which need to be processed concurrently by both threads.
Solution concept:
Use a simple boolean variable as start condition. Embed it together with the corresponding access mutex and the condition variable within a thin wrapper of type WaitableCondition:
class WaitableCondition
{
public:
WaitableCondition (bool in_autoReset = true)
:
m_autoReset (in_autoReset),
m_condIsTrue (false)
{}
void Set (bool in_state)
{
{
std::lock_guard<std::mutex> guard(m_mutex);
m_condIsTrue = in_state;
}
if (in_state) m_conditionVar.notify_one();
}
bool IsTrue (void)
{
std::lock_guard<std::mutex> guard(m_mutex);
return m_condIsTrue;
}
void WaitUntilTrue (void)
{
std::unique_lock<std::mutex> uLock(m_mutex);
m_conditionVar.wait(uLock,[]{return m_condIsTrue});
if (m_autoReset) m_condIsTrue = false;
}
bool TimedWaitUntilTrue (long in_timeoutMs)
{
std::chrono::milliseconds timeoutPeriod (in_timeoutMs);
std::unique_lock<std::mutex> uLock(m_mutex);
if (m_conditionVar.wait_for(uLock,timeoutPeriod,
[]{return m_condIsTrue}))
{
if (m_autoReset) m_condIsTrue = false;
return true;
}
else
{
return false;
}
}
private:
bool m_condIsTrue;
std::mutex m_mutex;
std::condition_variable m_conditionVar;
bool m_autoReset;
}
Autoreset
By using the autoreset feature of class WaitableCondition you could trigger thread B several times without any code changes.
Using class WaitableCondition
MySyncUtils::WaitableCondition myWaitableCondition;
Thread A triggers thread B
{
myWaitableCondition.Set(true);
}
Thread B waits to be triggered by thread A:
{
..
myWaitableCondition.WaitUntilTrue();
}
Because of the embedded logic within WaitableCondition it is not necessary that thread B has already entered waiting state before thread A signals a change of condition.