In the Linux system, threads are lightweight execution units, the correct destruction of threads can avoid memory leaks and other problems, Linux threads have joinable and detached two kinds of properties.
In my previous article, I covered some historical background of Linux threads and the NPTL thread model. In this article, the correct way to destroy Linux threads will be explained with the above knowledge in mind.
In Linux, threads are lightweight execution units, and destroying them properly can avoid memory leaks and other problems. The reason for writing this article is that the production environment reported some memory leaks, and after troubleshooting, we found that it was because the threads were not destroyed correctly: the threads were used somewhere, and would only be triggered once during normal testing, but when they came to the line, the user might not close the app for a very long time, and this time was long enough to eventually cause a rather obvious memory leak.
Recall that there are several functions related to the Linux thread life cycle:
Several ways to exit a thread
There are several ways to make a thread exit at the end of a thread.
When a thread function reaches the end of its execution or encounters a return statement, the thread will exit automatically. This is the most common way to exit a thread.
Calling pthread_exit immediately terminates the execution of the current thread. The pthread_exit function takes a pointer argument, which is passed as the thread’s return value to pthread_join the thread’s other threads.
Unlike direct returns, pthread_exit can be called anywhere the thread logic requires, not just the thread’s entry function. If pthread_exit is called in the main function, it will cause the process to exit.
Calling pthread_cancel sends a cancellation request to the specified thread. Unlike pthread_exit, which can only terminate the thread, pthread_cancel can be called by another thread to explicitly initiate a cancellation request to the target thread, and pthread_cancel is not mandatory, but rather is a negotiation mechanism whereby the target thread has the choice of whether or not to respond to the cancellation.
There are several APIs related to pthread_cancel:
Sets the cancellability state of the current thread, the available values are
Sets the handling of the current thread when it receives a cancellation. The available values are
PTHREAD_CANCEL_DEFERRED (terminate the thread when it continues to the
cancellable point) and
PTHREAD_CANCEL_ASYNCHRONOUS (terminate immediately).
PTHREAD_CANCEL_ENABLE point. Only makes sense if the cancellable state is
PTHREAD_CANCEL_ENABLE and the cancellation processing is
PTHREAD_CANCEL_DEFERRED, which will terminate the thread.
joinable and detached threads
Linux threads have two attributes: joinable and detached, which determine what happens to a thread’s resources after it exits.
joinable is the default property of threads. joinable threads do not release their resources (mainly thread-related memory structures, thread descriptors, etc.) until another thread waits and reclaims the thread’s resources by calling the pthread_join function. If the pthread_join function is not called, the joinable thread will become a “zombie thread” after exiting, causing memory leaks and other problems.
The following is an example of the use of the joinable attribute thread:
When a thread is set to the detached attribute, its exit status and resources are automatically reclaimed by the system without the need for other threads to call the pthread_join function and wait for the thread to exit. detached threads are used in situations where you don’t need to care about the exit status of the thread or clean up work, which can improve the efficiency of the application and simplify the logic of the code.
The following is an example of the detached attribute thread:
You can also configure the detached property when the thread is created, as follows:
Properties are only valid if they are set before the thread is created; once the thread is created, it is not possible to change its properties.
- It is good programming practice to ensure that allocated memory and file descriptors are properly freed and closed at the end of a thread function, whether it is a joinable or detached thread;
- After pthread_create, use pthread_join or pthread_detach as needed to ensure that thread structures, etc. are properly freed.