[Thread] Thread concept and related function implementation

Table of contents

0. The concept of threads

1. Basic operations of threads

1.1 Creation of threads: pthread_create function

1.2 Thread waiting: pthread_join function

1.3 Thread separation: pthread_detach function

1.4 Thread exit: pthread_exit function

1.5 Thread cancellation: pthread_cancel function

1.6 Thread cancellation status: pthread_setcancelstate function

1.7 Thread cancellation point: pthread_testcancel function

1.8 Thread cancellation type: pthread_setcanceltype function

1.9 Thread exit cleaning functions: pthread_cleanup_push and pthread_cleanup_pop functions

1.9.1 When calling the pthread_exit function, the system automatically calls the thread cleanup function

1.9.2 When a thread is canceled, the system automatically calls the thread cleanup function

1.9.3 When calling the pthread_cleanup_pop function, the system automatically calls the thread cleaning function

Summarize:


0. The concept of threads

The concept of thread

  • Each process has its own data segment, code segment, and stack segment, which requires a large system overhead when creating, switching, and canceling processes.
  • To reduce overhead, threads evolved from processes.
  •  Threads exist in a process and share the resources of the process.
  •  A thread is an independent flow of control in a process, consisting of an environment (including a register bank and program counter) and a series of execution instructions.
  •  Each process has an address space and a thread of control.

Comparison of threads and processes

Scheduling:

        Threads are the basic unit of CPU scheduling and dispatch.

Have resources:

        Process is the basic unit of program execution and resource allocation in the system.

        The thread itself generally does not own resources (except for the essential program counter, a set of registers and the stack), but it can access the resources of the process to which it belongs, such as process code segments, data segments and system resources (open files, I /O devices, etc.).

System overhead:

        Multiple threads in the same process can share the same address space, so synchronization and communication between them become easier.

        During process switching, it involves saving the CPU environment of the entire current process and setting the CPU environment of the newly scheduled process; while thread switching only requires saving and setting the contents of a small number of registers, and does not involve memory management operations, so Can use system resources more efficiently and improve system throughput.

Concurrency:

        Not only can processes be executed concurrently, but multiple threads in a process can also be executed concurrently.

Summarize:

        Threads are generally called lightweight processes.

        A process can create multiple threads, and multiple threads share the resources of a process.

        When each process is created, the system will give it 4G virtual memory. The 3G user space is private, so when the process switches, the user space will also switch, which will increase the system overhead. Multiple threads in a process share the memory of a process. resources, so there is no need to switch these resources when thread switching, and the efficiency will be higher.

        The scheduling mechanism of threads is the same as that of processes, with multiple threads switching back and forth.

The uses of multithreading:

Design of multitasking programs:

        A program may have to handle applications and handle multiple tasks. If different processes are developed to handle them, the system overhead will be large, data sharing, and the program structure will be inconvenient. In this case, multi-threaded programming methods can be used.

Concurrent programming:

        A task may be divided into different steps to complete. These different steps may be loosely coupled, and may be completed synchronously and concurrently through mutual exclusion of threads. This allows you to create threads for different task steps.

Network programming:

        In order to improve the efficiency of network utilization, we may use multi-threading and use one thread for each connection to process.

data sharing:

  • Different threads in the same process share the data space of the process to facilitate data sharing between different threads.
  • In multi-CPU systems, true parallelism is achieved.

1. Basic operations of threads

  1. Just like every process has a process number, every thread also has a thread number.
  2. The process number is unique in the entire system, but the thread number is different. A thread is only valid in the process environment to which it belongs.
  3. The process number is represented by the pid_t data type and is a non-negative integer. The thread number is represented by the pthread_t data type.
  4. Some systems use a structure to represent it when implementing pthread_t, so it cannot be processed as an integer in a portable operating system implementation.

1.1 Creation of threads: pthread_create function

pthread_create function:

#include<pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);

Function:

        Create a thread.

parameter:

        thread: Thread identifier address.

        attr: Thread attribute structure address.

        start_routine: Entry address of the thread function.

        arg: Parameters passed to the thread function.

return value:

        Success: 0

        Failure: non-0

        The difference from fork is that the thread created by pthread_create does not start running at the same point as the parent thread, but starts running from the specified function. After the function finishes running, the thread exits.

        Threads depend on processes for their existence. If the process that created the thread ends, the thread will also end.

        The thread function program is in the pthread library, so the parameter -lpthread must be added when linking.

Code example:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>

void* thread_fun(void* arg)
{
    printf("son1 if running\n");
}
int main()
{
    printf("man if running \n");
    pthread_t thread;

    if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    while (1);
    return 0;
}

 Execution screenshot:

Verification of thread scheduling mechanism

Code example:

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


void* pthread_fun1(void* arg)
{
    printf("son1 if running\n");
    sleep(1);
    printf("*****************************\n");
}
void* pthread_fun2(void* arg)
{
    printf("son2 if running\n");
    sleep(1);
    printf("----------------------------\n");
}
int main()
{
    pthread_t thread1, thread2;

    if (pthread_create(&thread1, NULL, pthread_fun1, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }

    if (pthread_create(&thread2, NULL, pthread_fun2, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    while (1);
    return 0;
}

Execution screenshot:

 Parameter passing of thread processing function

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

int num = 100;
void* pthread_fun1(void* srg)
{
    printf("son1:num = %d\n", num);
    num++;
    int n = *(int*)srg;
    printf("son1 n = %d\n", n);
}
void* pthread_fun2(void* arg)
{
    sleep(1);
    printf("son2:num =%d\n", num);
    int n = *(int*)arg;
    printf("son2 n = %d\n", n);
}

int main()
{
    pthread_t thread1, thread2;
    int a = 555;

    if (pthread_create(&thread1, NULL, pthread_fun1, (void*)&a) != 0)
    {
        perror("'fail too pthred_creat");
        exit(1);
    }
    if (pthread_create(&thread2, NULL, pthread_fun2, (void*)&a) != 0)
    {
        perror("fail to pthread_crateee");
        exit(1);
    }
    while (1);
    return 0;
}

Execution screenshot:

1.2 Thread waiting: pthread_join function

pthread_join function

#include<pthread.h>

int pthread_join(pthread_t thread, void **retval);

Function:

        Wait for the sub-thread to end and recycle the sub-thread resources.

parameter:

        thread: The thread number being waited for.

        retval: The address of the pointer used to store the thread's exit status.

return value:

        Success: 0

        Failure: non-0

Code example:

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

void* thread_fun(void* arg)
{
    printf("son is running\n");
    sleep(3);
    printf("son will quit\n");
}
int main()
{
    printf("man is running\n");

    pthread_t thread;
    if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
    {
        perror("fail to pthhread_create");
        exit(1);
    }
    if (pthread_join(thread, NULL) != 0)
    {
        perror("fail toooo pthread_join");
        exit(1);
    }
    printf("man will quit\n");
    return 0;
}

Execution screenshot:

Get the child thread exit status value

Code example:

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

void* thread_fun(void* arg)
{
    static   int num = 666;
    printf("son is running\n");
    sleep(3);
    printf("son will quit\n");
    return (void*)&num;
}
int main()
{
    printf("man is running\n");

    pthread_t thread;
    if (pthread_create(&thread, NULL, thread_fun, NULL) != 0)
    {
        perror("fail to pthhread_create");
        exit(1);
    }
    int* num;
    if (pthread_join(thread, (void**)&num) != 0)
    {
        perror("fail toooo pthread_join");
        exit(1);
    }
    printf("ret_val = %d\n", *num);
    printf("man will quit\n");
    return 0;
}

Execution screenshot:

1.3 Thread separation: pthread_detach function

The combined and separated states of threads

Linux thread execution is different from Windows. pthread has two states:

Joinable or detached, threads are created in a joinable state by default.

        If the thread is in the joinable state, the stack and thread descriptor occupied by the thread (more than 8K in total) will not be released when the thread function returns and exits or when pthread_exit. These resources will only be released after you call pthread_join.

        If it is a thread in the detached state, these resources will be automatically released when the thread function exits or pthread_exit. Use the pthread_detach function to set the thread to the detached state.

After creating a thread, its resources should be recycled, but using the pthread_join function will block the caller, so Linux provides a thread separation function:

pthread_detach function

#include<pthread.h>

int pthread_detach(pthread_t thread);

Function:

        Detach the calling thread from the current process and make it an independent thread. When the thread terminates, the system will automatically reclaim its resources.

parameter:

        thread: thread number.

return value:

        Success: 0

        Failure: non-0 

Code example:

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

void* pthread_fun(void* arg)
{
    printf("son is running\n");
    sleep(2);
    printf("son will quite\n");
}
int main()
{
    printf("man is running\n");
    pthread_t thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create\n");
        exit(1);
    }
//这里就不需要再执行pthread_join了
    if (pthread_detach(thread) != 0)
    {
        perror("fail to pthread_detach\n");
        exit(1);
    }
    while (1)
    {
        printf("hello world\n");
        sleep(1);
    }


    return 0;
}

 Execution screenshot:

1.4 Thread exit: pthread_exit function

        In the process, we can call the exit function or _exit function to end the process. In a thread, we can stop its control flow without terminating the entire process through the following three methods.

  1. Thread returns from executing function
  2. The thread calls pthread_exit to exit the thread
  3. Threads can be canceled by other threads in the same process

Thread exit function: pthread_exit function

#include<pthread.h>

void pthread_exit(void *retval);

Function:

        Exit the calling thread.

parameter:

        retval: Pointer to store thread exit status.

Note:       

        Multiple threads in a process share the data segment of the process. Therefore, usually the resources occupied by the thread will not be released after exiting.

        If you want to release resources, the combined state must pass the pthread_join function, and the separated state is automatically released. Code example:

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

void* pthread_fun(void* arg)
{
    static char buf[] = "this thread has quied";
    printf("son if running\n");
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        if (i == 5)
        {
            //      pthread_exit(NULL);
            pthread_exit(buf);
        }
        printf("**********************\n");
        sleep(1);
    }
}
int main()
{
    printf("man is running\n");
    pthread_t thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    char* str;
    pthread_join(thread, (void**)&str);
    printf("str = %s\n", str);
    printf("man will quit\n");

    return 0;
}

Execution screenshot:

1.5 Thread cancellation: pthread_cancel function

Canceling a thread refers to the operation of canceling an executing thread.

#include<pthread.h>

int pthread_cancel(pthread_t thread);

Function:

        Cancel the thread.

parameter:

        thread: Target thread ID.

return value:

        Success: 0

        Failure: error number

The essence of the pthread_cancel function is to send a signal to the target thread thread to cause the target thread to exit.

This function only sends a termination signal to the target thread and does not wait for the cancellation of the target thread to complete before returning.

        However, successful transmission does not mean that the target thread will definitely terminate. When the thread is canceled, the cancellation attribute of the thread will determine whether and when the thread can be canceled.

The thread's cancellation status:

        That is, whether the thread can be canceled.

Cancellation point of thread:

        That is where the thread is canceled.

Cancellation type of thread:

        When a thread can be canceled, will it be canceled immediately or will it be canceled when it reaches the cancellation point?

Code example:

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

void* pthread_fun(void* arg)
{
    while (1)
    {
        printf("son is running\n");
        sleep(1);
    }
}

int main()
{
    pthread_t  thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    sleep(3);
//任意线程都可以尝试取消
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    return 0;

}

Execution screenshot:

1.6 Thread cancellation status: pthread_setcancelstate function

        Under Linux systems, threads can be canceled by default. When programming, you can set whether the thread can be canceled through the pthread_setcancelstate function.

#include<pthread.h>

int pthread_setcancelstate(int state, int *oldstate);

Function:

        Sets whether the thread can be canceled.

parameter:

        state: new state.

                PTHREAD_CANCEL_DISABLE: Cannot be canceled.

                PTHREAD_CANCEL_ENABLE: Can be canceled.

        oldstate: The memory address that saves the original cancelable state of the calling thread.

return value:

        Success: 0

        Failure: non-0

Code example:

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

void* pthread_fun(void* arg)
{
    //      pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
    while (1)
    {
        printf("son is running\n");
        sleep(1);
    }
}

int main()
{
    pthread_t  thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    sleep(3);
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    return 0;

}

Execution screenshot:

1.7 Thread cancellation point: pthread_testcancel function

        After a thread is canceled, the thread is not terminated immediately. By default, the thread cannot be terminated until it reaches the cancellation point. When programming, you can set the thread's cancellation point through the pthread_testcancel function.

void pthread_testcancel(void);

#include<pthread.h>
void pthread_testcancel(void);

Function: Set the cancellation point of the thread.

Parameters: none

Return value: None

When another thread cancels the thread that called this function, the canceled thread ends when it executes this function.

POSIX.1 guarantees that when a thread calls any function in Table 1 and Table 2, the cancellation point will appear.

Table 1:

 Table 2:

 Code example:

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

void* pthread_fun(void* arg)
{
    while (1)
    {
        printf("son is running\n");
        sleep(1);
        pthread_testcancel();
    }
}

int main()
{
    pthread_t  thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    sleep(3);
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    return 0;

}

Execution screenshot:

1.8 Thread cancellation type: pthread_setcanceltype function

        After a thread is canceled, the thread is not terminated immediately. By default, the thread cannot be terminated until it reaches the cancellation point. When programming, you can set whether the thread can be canceled immediately through the pthread_setcanceltype function.

#include<pthread.h>

int pthread_setcanceltype(int type, int *oldtype);

Function:

        type: type

                PTHREAD_CANCEL_ASYNCHRONOUS: Cancel immediately.

                PTHREAD_CANCEL_DEFERRED: Do not cancel immediately.

        oldtype: Save the memory address of the calling thread's original cancelable type.

return value:

        Success: 0

        Failure: non-0

Code example:

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

void* pthread_fun(void* arg)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);

    //      pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
    while (1)
    {
        printf("son is running\n");
        sleep(1);
    }
}

int main()
{
    pthread_t  thread;
    if (pthread_create(&thread, NULL, pthread_fun, NULL) != 0)
    {
        perror("fail to pthread_create");
        exit(1);
    }
    sleep(3);
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    return 0;

}

Execution screenshot:

1.9 Thread exit cleaning functions: pthread_cleanup_push and pthread_cleanup_pop functions

        Just like the exit cleanup of a process, a thread can also register a function to be called when it exits. Such a function is called a thread cleanup handler.

Notice:

        A thread can establish multiple cleanup handlers.

        Handlers are on the stack, so they are executed in the reverse order in which they were registered.

#include<pthread.h>

void pthread_cleanup_push(void (* routine)(void *), void*arg);

Function:

        Push the clear function onto the stack. That is, register the cleanup function.

parameter:

        routine: pointer to the thread cleanup function.

        arg: Parameters passed to the thread cleaning function.

Popup cleaning function

#include<pthread.h>

void pthread_cleanup_pop(int execute);

Function:

        The cleaning function will be popped from the stack, that is, the cleaning function will be deleted.

parameter:

        execute: Thread cleanup function execution flag.

        Non-0, the cleaning function pops up and the cleaning function is executed.

        0, the cleaning function pops up and the cleaning function is not executed.

The cleanup function is called when the thread performs the following actions:

  1. Call pthread_exit to exit the process.
  2. Respond to cancellation requests from other threads.
  3. Call pthread_cleanup_pop with non-zero execute

        In either case, pthread_cleanup_pop will delete the cleanup processing function tested by the last pthread_cleanup_push call.

1.9.1 When calling the pthread_exit function, the system automatically calls the thread cleanup function

Code example:

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

void mycleanup(void* arg)
{
    printf("cleann up ptr = %s\n", (char*)arg);
    free((char*)arg);
}
void* pthread_fun(void* arg)
{
    char* ptr = NULL;
    printf("this is new thread\n");
    ptr = (char*)malloc(100);
    pthread_cleanup_push(mycleanup, (void*)(ptr));
    memset(ptr, 0, 100);
    strcpy(ptr, "memory from malloc");
    sleep(3);
    printf("before exit\n");

    pthread_exit(NULL);
    printf("before pop\n");
    pthread_cleanup_pop(1);
}
int main()
{
    pthread_t thread;
    pthread_create(&thread, NULL, pthread_fun, NULL);
    pthread_join(thread, NULL);
    printf("process is dying\n");
    return 0;
}

Execution screenshot:

1.9.2 When a thread is canceled, the system automatically calls the thread cleanup function
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<string.h>

void mycleanup(void* arg)
{
    printf("cleann up ptr = %s\n", (char*)arg);
    free((char*)arg);
}
void* pthread_fun(void* arg)
{
    char* ptr = NULL;
    printf("this is new thread\n");
    ptr = (char*)malloc(100);
    pthread_cleanup_push(mycleanup, (void*)(ptr));
    memset(ptr, 0, 100);
    strcpy(ptr, "memory from malloc");
    sleep(10);

    //      pthread_exit(NULL);

    printf("before pop\n");
    pthread_cleanup_pop(1);
}
int main()
{
    pthread_t thread;
    pthread_create(&thread, NULL, pthread_fun, NULL);
    sleep(5);
    printf("before cancel\n");
    pthread_cancel(thread);
    pthread_join(thread, NULL);
    printf("process is dying\n");
    return 0;
}

 Execution screenshot:

1.9.3 When calling the pthread_cleanup_pop function, the system automatically calls the thread cleaning function

Code example:

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

void cleanup_func1(void* arg)
{
    printf("in clanup func1\n");
    printf("clean up ptr = %s\n", (char*)arg);
    free((char*)arg);
}
void cleanup_func2(void* arg)
{
    printf("in cleanup func2\n");
}
void* pthread_fun(void* arg)
{
    char* ptr = NULL;
    printf("this is new thread\n");
    ptr = (char*)malloc(100);
    pthread_cleanup_push(cleanup_func1, (void*)(ptr));
    pthread_cleanup_push(cleanup_func2, (void*)(ptr));
    memset(ptr, 0, 100);
    strcpy(ptr, "memory from malloc");

    sleep(3);
    printf("before pop\n");
    pthread_cleanup_pop(1);
    printf("before pop\n");
    pthread_cleanup_pop(1);
}
int main()
{
    pthread_t thread;
    pthread_create(&thread, NULL, pthread_fun, NULL);
    pthread_join(thread, NULL);
    printf("process is dying\n");
    return 0;
}

Execution screenshot:

Summarize:

         In this blog, we introduce the relevant functions in thread programming in detail. By understanding and mastering these functions, we can more flexibly create, manage, and synchronize threads to achieve concurrent programming and multitasking. These functions include thread creation, startup, waiting and termination, etc., and are key tools for writing multi-threaded applications.

        Mastering the use of thread functions can help us build efficient and reliable multi-threaded applications and make full use of computing resources to improve performance. The flexible application of these functions allows us to achieve coordination and communication between threads and avoid common problems in concurrent programming such as race conditions and deadlocks.

Guess you like

Origin blog.csdn.net/crr411422/article/details/131501862