I am using an std::thread
in my C++
code to constantly poll for some data & add it to a buffer. I use a C++ lambda
to start the thread like this:
StartMyThread() {
thread_running = true;
the_thread = std::thread { [this] {
while(thread_running) {
GetData();
}
}};
}
thread_running
is an atomic<bool>
declared in class header. Here is my GetData
function:
GetData() {
//Some heavy logic which needs to be executed in a worker thread
}
Next I also have a StopMyThread
function where I set thread_running
to false so that it exits out of the while loop in the lambda block
.
StopMyThread() {
thread_running = false;
the_thread.join();
}
It works well. The thread starts & stops without crashing.
This C++ code is used on iOS, Android, OS X and Windows. My application UI has a button which requires me to start & stop the thread on a button press; this button can be frequently used in some occasions. I can see a split second delay in UI while stopping or starting the thread.
My question is: In C++, is this a correct way to start/stop a thread frequently ? I think that with this logic I am creating a new thread every-time. And as I understand, creating a new thread makes the OS allocate lot of new resources which can be time-consoming. And I think this is the mistake I am doing. How can I avoid this ?
How can make use of the same thread without allocating new one repeatedly throughout the application lifecycle, and just play/pause it when required ?
This is the classical example for the use of a condition variable. You wait on a mutex and notify a thread when a certain condition is fulfilled; this way you don't need to allocate a new thread when you need one, but this is not always a good thing, if you wish to save memory. An alternative would be a coroutine yielding to another coroutine when data is needed, which is arguably prettier. You need to implement coroutines yourself, or use a ready-made library, such as boost.coroutine
.
::std::condition_variable cv_;
::std::mutex m_;
bool data_is_ready_{};
StartMyThread()
{
::std::thread([this]
{
for (;;)
{
::std::unique_lock<decltype(m_)> l(m_);
cv.wait(l, [this]{ return data_is_ready_; });
// do your stuff, m_ is locked
data_is_ready_ = false;
}
}
).detach();
}
To notify:
{
::std::unique_lock<decltype(m_)> l(m_);
data_is_ready_ = true;
}
cv_.notify_one();
As it is often faster to free the lock before notifying, than vice-versa.