I am having a trouble exiting a thread using a Restart function. When calling Stop it exits the thread, but Restart which calls Stop and then Start right after - doesn't exit the thread -> calls Start and creates a new thread.
Thanks. Any help would be really helpful and appreciated.
Dummy code to show the problem:
#include <iostream>
#include <thread>
#include <condition_variable>
#include <chrono>
using namespace std;
bool running = false;
unsigned int interval = 5000;
condition_variable cv_work;
mutex mu_cv_work;
void Start()
{
unique_lock<std::mutex> lock(mu_cv_work);
running = true;
lock.unlock();
thread([]{
cout << "new thread" << '\n';
while (running)
{
cout << "work..." << '\n';
unique_lock<std::mutex> lock(mu_cv_work);
cout << "sleep" << '\n';
if (cv_work.wait_for(lock, chrono::milliseconds(interval), []{return running == false;}))
{
cout << "exit thread" << '\n';
return;
}
cout << "done sleeping" << '\n';
}
}).detach();
}
void Stop()
{
unique_lock<std::mutex> lock(mu_cv_work);
running = false;
lock.unlock();
cv_work.notify_one();
}
void Restart()
{
Stop();
Start();
}
int main()
{
Start();
cout << "press to Stop" << '\n';
cin.get();
Stop(); // Stop actually exits the Thread
cout << "press to Start" << '\n';
cin.get();
Start();
cout << "press to Restart" << '\n';
cin.get();
Restart(); // Stop doesn't exit the Thread (Restart calls Stop() and Start())
return 0;
}
Output:
press to Stop
new thread
work...
sleep
// KEY PRESS
exit thread
press to Start
// KEY PRESS
new thread
work...
sleep
press to Restart
// KEY PRESS
new thread
work...
sleep
done sleeping
work...
sleep
Expected output:
press to Stop
new thread
work...
sleep
// KEY PRESS
exit thread
press to Start
// KEY PRESS
new thread
work...
sleep
press to Restart
// KEY PRESS
exit thread // THIS LINE
new thread
work...
sleep
done sleeping
work...
sleep
You designed your threads this way –
stopterminates the thread, and at this point it is gone –startcreates a new one and cannot do anything else as the other one is lost.Worse: those two threads (new and old one) might overlap in execution for some time.
You already have included a loop within your thread – now make sure that you do not exit this loop if you are going to restart your thread. You currently included a
returnwithin your code – this makes therunningthe test forrunningin your while loop obsolete anyway. We now can change a bit, reusing therunningvariable for another purpose; additionally we might use a three-fold state, possibly defined in an enum:Now you'll set the state to
Activeon starting a thread, on stopping you'd set the state toExitand notify the thread as is while on re-start you'd set the state appropriately, again notifying the thread. I personally would use an intermediate function for:Now you'll consider the state in your loop:
Be aware, though, that you should re-initialise the state of local variables as well (it's a restart, isn't it?) – at least as far as these don't need to be intentionally persisted over multiple thread starts. A pretty simple approach to achieve correct re-initialisation might be packing the variables into a double loop:
I still recommend to pack all this code into its own class. This comes with several advantages:
globalStatevariable and thenotifyfunction, the mutex, the condition variable and possibly many others).You should call
stopfrom within the destructor as well to make sure a thread is correctly stopped if theThreadRunnerinstance runs out of scope withoutstophaving been explicitly called:Note: Any code above is entirely untested; if you find a bug, please fix yourself.