What happens if you call exit (0) while other threads are still running?

Suppose a program has several threads: t1, t2, etc. They use pthreads. Stream t2 sits in a loop, reading from the stream and accessing a variable with a static storage duration.

Now suppose t1 calls exit(0) .

(More info: I have a program that does this on a Unix-based system, and compiled with g ++. The program seems to crash sometimes when it finishes working with a stack trace that points to an invalid static variable.)

  • Is the thread destroyed before the C ++ object is destroyed?

  • C ++ does not know about threads, so they continue to work until C ++ cleanup is complete?

  • Should the SIGTERM handler first disable or destroy threads before continuing, or does this happen automatically?

+15
source share
3 answers

I answer the question in the title of your question, and not three points with a marker, because I think that the answers to questions with a marker do not matter for the answer to the real question.

Using exit when a program is in a random state - as you seem to suggest - is usually a pretty cruel and non-deterministic way to terminate a program even with a single thread. It does not even matter if the stream is destroyed before or after the destruction of the object, both ways lead to nightmares. Remember that each thread can be in an arbitrary state and have access to everything. And the stack objects of each thread will not be destroyed properly.

See the exit documentation for what it does and what doesn't.

The best way I have seen the correct termination of a multithreaded program is to make sure that no thread is in a random state. Stop all threads in one way or another, call join for them where possible, and call exit - or return from the last remaining thread, if this happens in the main function.

The wrong approach, which I often saw, is to get rid of some objects correctly, close some descriptors, and generally try to do the correct shutdown until everything goes wrong, and then call terminate . I advise against this.

+5
source

Let me try to answer your questions. Guys, correct me if I'm wrong.

Your program crashes periodically. This is the expected behavior. You have released all acquired resources. And your stream, which is alive, is trying to access resources based on the information it has. If it is successful, it will be launched. If this fails, it will work.

Often the behavior would be sporadic. If the OS allocates the allocated resources to other processes or uses resources, you will see that your thread is crashing. If not, your thread is running. This behavior depends on the OS, hardware, RAM,% of the resources used at the completion of the process. Any use of resources, etc. Etc.

Is the thread deleted before the C ++ object is destroyed? No. In C ++ there is no built-in thread support. P-threads are only posix threads that work with the underlying OS and provide you with the functionality to create threads if necessary. From a technical point of view, since threads are not part of C ++, threads that are automatically killed are not possible. Correct me if I am wrong.

Is C ++ aware of threads, so they continue to work until C ++ cleanup is complete? C ++ does not know about threads. The same cannot be said for C ++ 11

Should the SIGTERM handler first disable or kill threads before continuing, or will this happen automatically? Technically, a SIGTERM handler should not kill threads. Why do you want OS handlers to kill current threads? Each operating system runs on hardware to provide functionality for users. Do not kill any of the running processes. Well, programmers should join threads basically, but there may be times when you want your threads to run for a while. May be.

The responsibility of the software developer / provider is to write code that does not break and does not end in endless loops, and if necessary, kill all executed threads. The OS cannot take responsibility for these actions. For this reason, Windows / Apple certifies some software for its OS. Thus, customers can buy it with peace of mind.

+2
source

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) { // ...setup attr... m_thread = pthread_create(&m_thread, &attr, &start_func, this); } ~thread() { stop(); } void stop() { // TODO: make sure that a second call works... m_runner->stop(); pthread_join(m_thread); } private: static void start_func(void * data) { ((thread *) data)->start(); } void start() { m_runner->run(); } runner * m_runner; pthread_t m_thread; }; 

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.

0
source

Source: https://habr.com/ru/post/977886/


All Articles