Sample that uses condition variables.
Conditional variables generally use certain combinations of the following values.
-
Condition variable (
CondVarState::cond
)
-
Lock (
CondVarState::lock
)
-
Exclusive control state (
CondVarState::flag
)
Follow the steps below to wait to be notified from other threads.
-
Apply a lock.
-
Pass a lock and wait. The lock is released internally.
-
When notified, the lock is automatically reacquired. Check the state (to account for waiting after notification), and execute if possible.
-
Change the state to non-signal (when appropriate).
Follow the steps below to notify other threads.
-
Apply a lock.
-
Change to the signal state.
-
Call
nn::nlib::threading::CondVar::Notify
or nn::nlib::threading::CondVar::NotifyAll()
to give notification to other threads.
-
Unlock.
This is written in the ThreadParam::Exec
function.
The even and odd threads signal each other to continue the process in this sample.
using nlib_ns::threading::ScopedLock;
using nlib_ns::threading::UniqueLock;
using nlib_ns::threading::CondVar;
using nlib_ns::threading::SimpleCriticalSection;
using nlib_ns::threading::Thread;
const int kNumThread = 6;
Thread g_th[kNumThread];
struct CondVarState {
CondVar cond;
SimpleCriticalSection lock;
bool flag;
public:
CondVarState() : flag(false) {}
} g_cond1, g_cond2;
class ThreadParam {
public:
ThreadParam() : thread_num_(0), is_success_(false), wait_(NULL), signal_(NULL) {}
void Initialize(int num, CondVarState* w, CondVarState* s) {
thread_num_ = num;
wait_ = w;
signal_ = s;
is_success_ = true;
}
void Exec();
static void ThreadFunc(ThreadParam* param) { param->Exec(); }
bool IsSuccessful() { return is_success_; }
private:
int thread_num_;
bool is_success_;
CondVarState* wait_;
CondVarState* signal_;
};
void ThreadParam::Exec() {
{
UniqueLock<SimpleCriticalSection> lock(wait_->lock);
while (!wait_->flag) {
errno_t e = wait_->cond.Wait(lock);
is_success_ = false;
return;
}
nlib_printf(
" Thread %d may be notified and acquires lock\n", thread_num_);
}
nlib_printf(
" Thread %d is notified, and do its own job\n", thread_num_);
wait_->flag = false;
}
{
ScopedLock<SimpleCriticalSection> lock(signal_->lock);
signal_->flag = true;
signal_->cond.NotifyAll();
}
}
bool CondVarDemo() {
ThreadParam param[kNumThread];
for (int i = 0; i < kNumThread; ++i) {
if (i % 2 == 0) {
param[i].Initialize(i, &g_cond1, &g_cond2);
e = g_th[i].Start(ThreadParam::ThreadFunc, ¶m[i]);
} else {
param[i].Initialize(i, &g_cond2, &g_cond1);
e = g_th[i].Start(ThreadParam::ThreadFunc, ¶m[i]);
}
}
g_cond1.lock.lock();
g_cond1.flag = true;
g_cond1.cond.NotifyAll();
g_cond1.lock.unlock();
for (int i = 0; i < kNumThread; ++i) {
e = g_th[i].Join();
}
for (int i = 0; i < kNumThread; ++i) {
if (!param[i].IsSuccessful()) return false;
}
return true;
}
static bool SampleMain(int, char**) { return CondVarDemo(); }
NLIB_MAINFUNC