Both thread safety and liveness are concerns when using condition variables. The thread-safety property requires that all objects maintain consistent states in a multithreaded environment [Lea 2000]. The liveness property requires that every operation or function invocation execute to completion without interruption; for example, there is no deadlock.
...
This noncompliant code example uses five threads that are intended to execute sequentially according to the step level assigned to each thread when it is created (serialized processing). The currentStep
variable holds the current step level and is incremented when the respective thread completes. Finally, another thread is signaled so that the next step can be executed. Each thread waits until its step level is ready, and the wait()
call is wrapped inside a while
loop, in compliance with CON54-CPP. Wrap functions that can spuriously wake up in a loop.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <condition_variable> #include <iostream> #include <mutex> #include <thread> std::mutex mutex; std::condition_variable cond; void run_step(size_t myStep) { static size_t currentStep = 0; std::unique_lock<std::mutex> lk(mutex); std::cout << "Thread " << myStep << " has the lock" << std::endl; while (currentStep != myStep) { std::cout << "Thread " << myStep << " is sleeping..." << std::endl; cond.wait(lk); std::cout << "Thread " << myStep << " woke up" << std::endl; } // Do processing... std::cout << "Thread " << myStep << " is processing..." << std::endl; currentStep++; // Signal awaiting task. cond.notify_one(); std::cout << "Thread " << myStep << " is exiting..." << std::endl; } int main() { constexpr size_t numThreads = 5; std::thread threads[numThreads]; // Create threads. for (size_t i = 0; i < numThreads; ++i) { threads[i] = std::thread(run_step, i); } // Wait for all threads to complete. for (size_t i = numThreads; i != 0; --i) { threads[i - 1].join(); } } |
...