[Linux] Connection to a terminated thread (pthread_join), thread separation (pthread_detach), thread cancellation (pthread_detach)

Table of Contents

  • 1. Connection to terminated thread (pthread_join)
      • function analysis
      • code example
  • 2. Separation of threads
      • function analysis
      • code example
  • 3. Thread cancellation
      • function analysis
      • code example
  • 4. Thread attributes
      • function analysis
      • code example

Orange

1. The connection terminated thread (pthread_join)

Function Analysis

/*
    #include <pthread.h>
    int pthread_join(pthread_t thread, void **retval);
        - Function: Connect with a terminated thread to reclaim the resources of the child thread
                This function is a blocking function, and only one child thread can be recycled at a time
                Generally used in the main thread
        - parameters:
            - thread: the ID of the child thread that needs to be recycled
            - retval: Receive the return value when the child thread exits
        - return value:
            0 : success
            Non-0: failure, returned error number
*/

Code Example

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

int value = 10;

void * callback(void * arg) {<!-- -->
    printf("child thread id : %ld\\
", pthread_self());
    // sleep(3);
    // return NULL;
    // int value = 10; // Local variables, different threads in a process divide different stack spaces, and when the sub-thread ends, the stack space belonging to the sub-thread is released.
    //So in the main thread later, there is no way to receive the return value of the sub-thread through pthread_join. So if you want to receive, value must be a global variable (not a local variable)
    pthread_exit((void *) & amp;value); // return (void *) & amp;value;
}

int main() {<!-- -->

    // Create a child thread
    pthread_t tid;
    int ret = pthread_create( &tid, NULL, callback, NULL);

    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error : %s\\
", errstr);
    }

    // main thread
    for(int i = 0; i < 5; i ++ ) {<!-- -->
        printf("%d\\
", i);
    }

    printf("tid : %ld, main thread id : %ld\\
", tid ,pthread_self());

    // The main thread calls pthread_join() to recycle the resources of the child thread, and receives the return value when the child thread exits through thread_retval
    // Note that if the sub-thread is not over, the main thread will always be blocked here waiting for recycling
    int * thread_retval;
    ret = pthread_join(tid, (void **) & thread_retval);

    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error : %s\\
", errstr);
    }

    printf("sub-thread return value: %d\\
", *thread_retval);

    printf("Successful recovery of child thread resources!\\
");

    // Let the main thread exit. When the main thread exits, it will not affect other normal running threads.
    pthread_exit(NULL);

    return 0;
}

Explain why pthread_join(tid, (void **) & amp;thread_retval) in the program passes in a secondary pointer, assuming that the address of value (value 10) is 0x0001, then I pass int * in the main program thread_retval defines a first-level pointer, and I need to modify the value of this first-level pointer to make it 0x0001. So I pass it into the pthread_join function, and the address of the first-level pointer (that is, the second-level pointer) must be passed in, so that the value of the first-level pointer can be changed to 0x0001 within the function. If only the first-level pointer is passed in, its value will change inside the function, but it will still not change outside the function.
For example, if I want to change the value of int a through a function, the input should be & amp;a, and that will work.

2. Separation of threads

Function Analysis

/*
    #include <pthread.h>
    int pthread_detach(pthread_t thread);
        - Function: detach a thread. When the detached thread terminates, it will automatically release resources and return them to the system.
          1. It cannot be separated multiple times, which will cause unpredictable behavior.
          2. If you cannot connect to a thread that has been separated, an error will be reported.
        - Parameter: ID of the thread that needs to be detached
        - return value:
            Success: 0
            Failed: return error number
*/

Code Example

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void * callback(void * arg) {<!-- -->
    printf("chid thread id : %ld\\
", pthread_self());
    return NULL;
}

int main() {<!-- -->

    // Create a child thread
    pthread_t tid;

    int ret = pthread_create( & tid, NULL, callback, NULL);
    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error1 : %s\\
", errstr);
    }

    // Output the id of the main thread and sub-thread
    printf("tid : %ld, main thread id : %ld\\
", tid, pthread_self());

    // Set the sub-thread separation. After the sub-thread is separated, the corresponding resources at the end of the sub-thread do not need to be released by the main thread
    ret = pthread_detach(tid);
    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error2 : %s\\
", errstr);
    }

    // After setting the separation, connect the separated sub-threads pthread_join()
    // ret = pthread_join(tid, NULL);
    // if(ret != 0) {<!-- -->
    // char * errstr = strerror(ret);
    // printf("error3 : %s\\
", errstr);
    // }

    pthread_exit(NULL);

    return 0;
}

The main thread creates a child thread. The default attribute of the thread is a non-separated state. The main thread waits for the child thread to end. Only when the pthread_join() function returns, the created child thread is terminated and the system resources occupied by itself can be released. The detached state means that the thread will not be waited by other threads. After running by itself, the thread will end, and the system resources will be released immediately.
My understanding is that if the thread ends first and then runs to the pthread_detach() function, this function will also work, because the thread has finished running, and it does not mean that the resource will be released. If the pthread_join() function is not called, it needs to become a detached state to release resources.

3. Thread cancellation

Function Analysis

/*
    #include <pthread.h>
    int pthread_cancel(pthread_t thread);
        - Function: cancel the thread (let the thread terminate)
            Canceling a thread can terminate the running of a thread,
            But it does not terminate immediately, but when the child thread executes to a cancellation point, the thread will terminate.
            Cancellation point: Some system calls stipulated by the system can be roughly understood as switching from the user area to the kernel area. This position is called the cancellation point.
        - Application: For example, to clean up garbage, a thread will be opened for execution. If you don’t want to clean up in the middle, click Cancel to terminate the thread
*/

Code Example

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void * callback(void * arg) {<!-- -->
    printf("chid thread id : %ld\\
", pthread_self());
    for(int i = 0; i < 5; i ++ ) {<!-- -->
        printf("child : %d\\
", i);
    }
    return NULL;
}

int main() {<!-- -->
    
    // Create a child thread
    pthread_t tid;

    int ret = pthread_create( & tid, NULL, callback, NULL);
    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error1 : %s\\
", errstr);
    }

    // cancel thread
    pthread_cancel(tid);

    for(int i = 0; i < 5; i ++ ) {<!-- -->
        printf("%d\\
", i);
    }

    // Output the id of the main thread and sub-thread
    printf("tid : %ld, main thread id : %ld\\
", tid, pthread_self());

    
    pthread_exit(NULL);

    return 0;
}

It can be seen from the result that the main function of has finished executing all the content, and finally the child thread only executes printf, and does not execute the printf under the for loop. This means that the printf under the for loop is a system-defined cancellation point (note that pthread_cancel is a non-blocking function, so after calling this function, even if the child thread has not terminated, the main thread can still continue run)

And every time you run it, the result may be different

4. Thread attributes

The main function is also to set thread separation, but the function is different from the above. And some other functions to get thread-related properties.

Function Analysis

/*
    int pthread_attr_init(pthread_attr_t *attr);
        - Initialize thread attribute variables

    int pthread_attr_destroy(pthread_attr_t *attr);
        - Release the resources of the thread attribute

    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
        - Get the status attribute of the thread detached

    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
        - Set the status property of the thread detached
*/

Code Example

#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>

void * callback(void * arg) {<!-- -->
    printf("chid thread id : %ld\\
", pthread_self());
    return NULL;
}

int main() {<!-- -->

    // Create a thread attribute variable
    pthread_attr_t attr;
    // Initialize attribute variables
    pthread_attr_init( &attr);

    // Set properties, PTHREAD_CREATE_DETACHED represents thread separation
    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED);

    // Create a child thread
    pthread_t tid;

    int ret = pthread_create( &tid, &attr, callback, NULL);
    if(ret != 0) {<!-- -->
        char * errstr = strerror(ret);
        printf("error1 : %s\\
", errstr);
    }

    // Get the size of the thread's stack
    size_t size;
    pthread_attr_getstacksize( &attr, &size);
    printf("thread stack size : %ld\\
", size);

    // Output the id of the main thread and sub-thread
    printf("tid : %ld, main thread id : %ld\\
", tid, pthread_self());

    // Release the thread attribute resource
    pthread_attr_destroy( &attr);

    pthread_exit(NULL);

    return 0;
}