Reputation: 7368
In my main function there are some objects that do works in background until the destructor is called like in the following snippet:
#include <iostream>
class MyBackground {
public:
MyBackground()
{
m_workerThread = std::thread(&MyBackground::work, this);
}
~MyBackground()
{
g_exit = true;
workerThread.join();
}
private:
void work()
{
while(!m_exit);
}
private:
std::atomic<bool> m_exit{false};
std::thread m_workerThread;
};
int main(int argc, char* argv[])
{
MyBackground object;
// here ther's some async background work
return EXIT_SUCCESS;
// ~MyBackground -> here threads are stopped
}
I need a way to block main until some extern signal occurs. My first attempt was something like this:
#include <csignal>
#include <iostream>
#include <thread>
using namespace std
atomic<bool> g_Exit{false};
void signalExit(int)
{
g_Exit = true;
}
int main(int argc, char* argv[])
{
signal(SIGINT, signalExit);
signal(SIGTERM, signalExit);
MyBackground object;
while (!g_Exit)
this_thread::sleep_for(chrono::seconds{1});
// here ther's some async background work
return EXIT_SUCCESS;
// ~MyBackground -> here threads are stopped
}
But I'm not sure this works well. I thought is better use condition_variables
like the following snippet:
#include <csignal>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std
bool g_exitFlag = false;
condition_variable g_exitCondition;
mutex g_exitMutex;
using Lock = unique_lock<mutex>;
void signalExit(int)
{
Lock lock{g_exitMutex};
g_exitFlag = true;
g_exitCondition.notify_one();
}
int main(int argc, char* argv[])
{
signal(SIGINT, signalExit);
signal(SIGTERM, signalExit);
MyBackground object;
Lock lock{g_exitMutex};
g_exitCondition.wait(lock, [](){return g_exitFlag;});
// here ther's some async background work
return EXIT_SUCCESS;
// ~MyBackground -> here threads are stopped
}
Which of the best implementations is correct. Are them correct? I'm not "experts" in multithreading programming.
Upvotes: 2
Views: 1320
Reputation: 409166
Why not a slightly different design where your background worker thread isn't created and destroyed by the MyBackground
class, but the thread is created in the main
function? Then the main
function can simply call join
on the thread before it exits, and it will block until the background thread is done.
Something like
class MyBackground {
public:
void work()
{
while(!m_exit)
{
// Do background work
}
}
private:
std::atomic<bool> m_exit{false};
};
int main()
{
MyBackground background;
std::thread background_thread(&MyBackground::work, background);
// Do foreground work
background.join(); // Wait for background to exit
// Implicit return
}
Another solution, if the thread is supposed to continue when the main
function returns, is to detach the thread. Then it will work independent of the main thread, and the process will not actually exit until the thread has exited.
Note that this requires the main
function to not just return or exit
, as that will end the process including killing all threads. Instead you need to just exit the "main" thread. Unfortunately this is not possible using the standard C++ thread interface, but you have to use platform native functions. For example on POSIX systems (like macOS and Linux) you use pthread_exit
.
Upvotes: 4
Reputation: 5156
You can simply use a std::weak_ptr in main() to check if the strong ref count is nil to terminate. Pass the only strong reference pointer to other threads to enable main() to exit smoothly. Use std::conditon_variable instead to avoid closing threads waiting on condition_variable to reduce CPU usage during polling for checking if the weak_ptr expires or not.
void worker_thread(std::shared_ptr<int>& pulse)
{
// do something
std::this_thread::sleep_for(std::chrono::seconds(2));
pulse.reset(); // reset to terminate main() too from any other threads
}
void main()
{
std::shared_ptr<int> pulse = std::make_shared<int>(1);
std::weak_ptr<int> weak_pulse(pulse);
std::thread([&](){ worker_thread(pulse); }).detach();
while (auto strong = weak_pulse.lock())
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
Upvotes: 0