Starting with C ++ 11, we have std::thread , and since then we can say that C ++ knows about threads. However, these may not be pthreads (this is under Linux, but this is an implementation detail), and you specifically mentioned that you used pthreads ...
One thing I wanted to add from Chris's answer is that actually working with threads is a bit more complicated than it seems at first glance. In most cases, people create one class for RAII (Resource Acquisition Is Initialization.)
Here is an example with a memory block to keep it simple (but consider using std::vector or std::array to control the buffer in C ++):
class buffer { public: buffer() : m_buffer(new char[1024]) { } ~buffer() { delete [] m_buffer; } char * data() { return m_buffer; } private: char * m_buffer; };
This class controls the m_buffer pointer m_buffer . During construction, it allocates a buffer, and upon destruction, releases it. Until here, nothing new.
However, you have a potential problem with threads, because the class in which the thread is running must remain in good condition before it is destroyed, and as not many C ++ programmers know once you reach the destructor, it's too late ... in particular by invoking any virtual functions.
Thus, the base class, as it should, is actually incorrect:
// you could also hide this function inside the class, see "class thread" below class runner; void start_func(void * data) { ((runner *) data)->run(); } class runner { public: runner() { // ...setup attr... m_thread = pthread_create(&m_thread, &attr, &start_func, this); } virtual ~runner() { stop(); // <-- virtual function, we may be calling the wrong one! pthread_join(m_thread); } virtual void run() = 0; virtual void stop() { m_stop = true; } private: pthread_t m_thread; bool m_stop = false; };
This is incorrect because the stop() function may require a call to some virtual function defined in your derived version of the class. Also, your run() function is likely to use virtual functions before it is created. Some of which may be purely virtual functions. A call to these functions in ~runner() was called by std::terminate() .
The solution to this problem is to have two classes. Runner with this purely virtual run() function and thread. The thread class is responsible for removing the runner after pthread_join() .
The slider is redefined to not include anything about pthread:
class runner { public: virtual void run() = 0; virtual void stop() { m_stop = true; } private: bool m_stop = false; };
The thread class handles stop() and this can happen in its destructor:
class thread { public: thread(runner *r) : m_runner(r) {
Now, when you want to use your runner, you overload it and implement the run() function:
class worker : runner { public: virtual void run() { ...do heavy work here... } };
Finally, you can use it safely when you are sure that the stream will be deleted first. This means a certain second (which classes use, since you need to pass the runner to the stream!)
int main() { worker w; thread t(&w); ...do other things... return 0; }
Now in this case, C ++ takes care of cleaning up, but only because I used return and not exit() .
However, exceptions are the solution to your problem. My main() also safe for exceptions! The thread will be purely stopped before std::terminate() is called (because I don't have try / catch, it will be interrupted).
One way to “exit from anywhere” is to create an exception that allows you to do this. Thus, main() will look something like this:
int main() { try { worker w; thread t(&w); ...do other things... } catch(my_exception const & e) { exit(e.exit_code()); } return 0; }
I am sure many people will comment on the fact that you should never use an exception to exit your software. The fact is that in most of my programs there is such an attempt / catch, so I can at least register the error that occurred, and it is very similar to using an "exit exception".
ATTENTION: std::thread is different from my thread class above. It takes a function pointer to execute some code. It will call pthread_join() (at least in g ++ Linux) on destruction. However, it does not say anything to your stream code. If you need to listen to some signal to find out that it should go out, you answer. This is a completely different way of thinking, but it can also be used safely (with the exception of this missed signal).
For a full implementation, you can see my snap_thread.cpp / h file on Github in Snap! C ++ project. My implementation includes many more features, in particular FIFO, which you can use to safely transfer workloads to your threads.
How about disconnecting a topic?
I also used this for a while. The fact is that only pthread_join() is 100% safe. Disabling means that the thread is still running, and exiting the main process may cause the thread to crash. Although, in the end, I would tell the thread to go out and wait for the "ready" signal to set, but it still crashes from time to time. It may take about 3 months or so before I see an unexplained accident, but it will happen. Since I deleted this and always use the union, I do not see these inexplicable crashes. Good proof that you don’t want to use this special thread disconnect function.