I am working on a program that handles mouse movement, it consists of two threads, the main thread gets the input and stores the mouse position in a fixed location and the child thread loops through that location to get the value:
void Engine::InputManager::MouseMove(const MouseMoveEvent& ev)
{
cur_mouse_ev_.x_ = ev.x_;
cur_mouse_ev_.y_ = ev.y_;
cv_.notify_all();
}
void Engine::InputManager::ProcessInput(MouseMoveEvent* ev)
{
while (true)
{
cv_.wait(u_mutex_);
float dx = static_cast<float>(ev->x_ - pre_mouse_pos[0]) * 0.25f;
float dy = static_cast<float>(ev->y_ - pre_mouse_pos[1]) * 0.25f;
g_pGraphicsManager->CameraRotateYaw(dx);
pre_mouse_pos[0] = ev->x_;
pre_mouse_pos[1] = ev->y_;
}
}
How do I reduce CPU utilization, I am using conditional variables to achieve this, is there a better way to do this? It seems that adding a delay to the subthreads would also work
Using std::condition_variable
is a good and efficient way to achieve what you want.
However - you implementation has the following issue:
std::condition_variable
suffers from spurious wakeups. You can read about it here: Spurious wakeup - Wikipedia.
The correct way to use a condition variable requires:
To add a variable (bool
in your case) to hold the "condition" you are waiting for. The variable should be updated under a lock using the mutex.
Again under a lock: calling wait
in a loop until the variable satisfies the condition you are waiting for. If a spurious wakeup will occur, the loop will ensure getting into the waiting state again. BTW - wait
method has an overload that gets a predicate for the condition, and loops for you.
You can see some code examples here: Condition variable examples.
A minimal sample that demonstrates the flow:
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cond_var;
bool ready{ false };
void handler()
{
{
std::unique_lock<std::mutex> lck(mtx);
cond_var.wait(lck, []() { return ready; }); // will loop internally to handle spurious wakeups
}
// Handle data ...
}
void main()
{
std::thread t(handler);
// Prepare data ...
std::this_thread::sleep_for(std::chrono::seconds(3));
{
std::unique_lock<std::mutex> lck(mtx);
ready = true;
}
cond_var.notify_all();
t.join();
}
Note:
Accessing the data (both when creating it and consuming it) must be done with proper synchronization (either holding the mutex
or in any other appropriate way).
The minimum that is required is to hold the mutex
while copying the data in the handler
and then process the copy.
Atlertatively the data can be accessed without a copy, but with proper synchronization all the way.
This is left out of the example above for simplicity.