I have several threads working on different problems at the same time.
Sometimes some threads are waiting for the results of other threads, whose status is set by a boolean variable.
When a thread has to wait for the result of another thread, I do it like this:
while(!finished)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
I'm looking for a better way to signal the scheduler to proceed to the next thread. Something like this:
while(!finished)
{
schedular.schedule();
}
What is the best way to signal to the scheduler that the current thread has nothing more to do and that it should continue with the next thread?
I have several threads working on different problems at the same time.
Sometimes some threads are waiting for the results of other threads, whose status is set by a boolean variable.
When a thread has to wait for the result of another thread, I do it like this:
while(!finished)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
I'm looking for a better way to signal the scheduler to proceed to the next thread. Something like this:
while(!finished)
{
schedular.schedule();
}
What is the best way to signal to the scheduler that the current thread has nothing more to do and that it should continue with the next thread?
Share Improve this question asked Jan 20 at 9:50 ThomasThomas 2,2594 gold badges24 silver badges45 bronze badges 4- 4 Have a look at std::future/std::asynce, std::condition_variable, std::latch, std::barrier, which is best depends on your actual design/use case. Since you use sleep_for I think std::condition_variable::wait (+predicate) is most likely. Though std::async/std::future might work too – Pepijn Kramer Commented Jan 20 at 9:53
- @PepijnKramer thanks a lot! std::condition_variable::wait seems to be the thing I was looking for =). I'll also take a look at the other sync possibilities, maybe I'll change my design. – Thomas Commented Jan 20 at 9:59
- I thought it might be, just be careful always use a predicate in wait. Condition variables have this "spurious wakeup" thing where they can fall through even if the condition is not true (which the predicate will handle) – Pepijn Kramer Commented Jan 20 at 10:02
- 1 Using a promise or a condition variable is good for isolated use-cases, but if the entire system is using them then I would really suggest you look into a task based scheduler instead of a thread-based one, overcomitting threads downgrades performance, ideally a system shouldn't have those signals, just tasks and dependencies. – Ahmed AEK Commented Jan 20 at 10:12
1 Answer
Reset to default 2The approach you're currently using with std::this_thread::sleep_for
introduces unnecessary delays and CPU usage, even with a small sleep duration. A better way to handle this scenario is to use synchronization primitives provided by the C++ Standard Library, such as std::condition_variable
. These allow a thread to wait efficiently, yielding execution to the scheduler until it is notified.
Using std::condition_variable
Here’s how you can rewrite your code using std::condition_variable
:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <atomic>
std::atomic<bool> finished{false}; // Atomic variable for thread-safe status
std::mutex mtx; // Mutex to protect condition variable
std::condition_variable cv; // Condition variable for signaling
void worker_thread() {
// Simulate some work
std::this_thread::sleep_for(std::chrono::seconds(2));
// Notify that the work is finished
{
std::lock_guard<std::mutex> lock(mtx);
finished = true;
}
cv.notify_one(); // Notify waiting thread
}
void waiting_thread() {
// Wait for the result
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return finished.load(); }); // Wait until finished is true
std::cout << "Work is finished, proceeding..." << std::endl;
}
int main() {
std::thread t1(worker_thread);
std::thread t2(waiting_thread);
t1.join();
t2.join();
return 0;
}
How It Works:
Condition Variable (
std::condition_variable
):- The
cv.wait
method efficiently puts the thread to sleep and releases the lock on the mutex while waiting. - The thread is woken up only when
cv.notify_one
orcv.notify_all
is called, reducing CPU usage compared to a busy-wait loop.
- The
Atomic Variable (
std::atomic<bool>
):- Ensures the
finished
flag is updated in a thread-safe manner without needing additional synchronization.
- Ensures the
Mutex (
std::mutex
):- Protects access to shared state (
finished
) and is required forstd::condition_variable
.
- Protects access to shared state (
Advantages:
- Efficiency: The thread doesn’t spin in a loop but is put to sleep until notified.
- Scheduler-Friendly: It allows the thread to yield its execution completely until there’s meaningful work to do.
- Scalable: Handles many threads without excessive CPU overhead.
This pattern is the recommended approach when dealing with threads that depend on each other's results. It minimizes CPU usage and leverages the operating system's efficient thread scheduling mechanisms.