Multi-threading (1): thread creation, recycling, separation

1. Multithreading overview

Multithreading is used very frequently during project development because it 多线程can be 提高programmed 并发性. There are two ways to improve program concurrency: (1)多线程( 2)多进程. However 多线程对系统资源的消耗会更加少一些, the execution efficiency of threads and processes is similar.

  • When executing system applications, the application needs to occupy CPU resources, and the CPU cores on the computer are limited. Suppose there is an 8-core CPU on the PC, but 100 applications are executed at the same time. 为什么看起来这100个执行程序可以同时运行呢?这其实是一个假象, the cpu will divide the unit time into several parts, and each part is a cpu time slice, cpu每个时间片很短,是纳秒级别. After the cpu time slice is divided, it will be scheduled by the system. When each thread is executed, 需要抢cpu的时间片if it is grabbed, the thread will execute ( 运行态). If it is not grabbed, the thread is in 就绪态the ready state and does not have the right to use the cpu, because it has not grabbed the time slice of the cpu, but it will keep grabbing it, and the ready state will change to running after grabbing it. state. After the time slice is used up, it becomes ready again, and continues to compete with other threads for time slices, and the cycle repeats.
  • 通过线程的快速切换,我们看到这个线程是一直在运行,其实这个线程也是在走走停停的,只不同我们肉眼无法识别这么短的时间。

linux的多线程在底层的实现方式和windows其实存在一些区别, because linux did not have the concept of multithreading in the early days. At the beginning of its creation, the Linux operating system was a process-based operating system. Because the frequency and efficiency of threads in windows are very high, the concept of threads is introduced in linux. Since the linux kernel did not design threads at the beginning of the design, therefore linux系统就基于进程做出来线程. Inherited in linux thread is a lightweight process (LWP: light weight process).

In the Linux environment, the essence of a thread is still a process. The program running on the computer is a combination of a set of instructions and instruction parameters, and the instructions control the operation of the computer according to the established logic. The operating system allocates system resources in units of processes. It can be understood that a process is the smallest unit of resource allocation, and a thread is the smallest unit of operating system scheduling and execution.

Let's start with a conceptual understanding of the difference between threads and processes:

  • 进程有自己独立的地址空间,多个线程共用同一个地址空间
    • Threads save system resources more, and the efficiency can not only be maintained, but also higher
    • Multiple threads in one address space are exclusive: each thread has its own stack area, registers (managed in the kernel)
    • Multiple threads share in one address space: code segment, heap area, global data area, open files (file descriptor table) are all shared by threads
  • 线程是程序的最小执行单位,进程是操作系统中最小的资源分配单位
    • Each process corresponds to a virtual address space,一个进程只能抢一个 CPU 时间片
    • 一个地址空间中可以划分出多个线程,在有效的资源基础上,能够抢更多的 CPU 时间片

insert image description here

  • If there is only one thread or process in an operating system, then no thread will compete with it for time slices, so all time slices belong to this thread, and it will run without interruption.
  • If there are multiple threads, when the cpu processes multiple threads, it will divide the cpu time into several time slices, each thread grabs a time slice, executes when it grabs it, gives up the right to use the cpu after execution, and then grabs the cpu time slice.
  • The orderly and alternate execution of multiple threads in the above figure is an ideal state, because 各个线程抢占cpu时间片都是随机的it is possible that thread 1 has grabbed the time slice for the first three times. Thread 2 did not grab the cpu time slice in the first 10 times. It is possible for thread 3 to grab the cpu time slice for the 2nd to 5th time, and each situation is random.因此不能在各个线程的执行状态认为是一个有序的执行,它是一个随机无序的执行状态。

CPU scheduling and switching: thread context switching is much faster than process

  • 上下文切换: Process/thread time-sharing multiplexes CPU time slices. Before switching, the state of the previous task will be saved (record the location of execution). When switching back to this task next time, this state will be loaded and continue to run. Loading this process again is a context switch.
  • 线程更加廉价,启动速度更快,退出也快,对系统资源的冲击小。Because multiple threads exit, only one address space needs to be destroyed in the end, that is, one resource is consumed. If multiple processes exit, you need to destroy multiple resources. So when it is released, whether the thread will be faster or not.

在处理多任务程序的时候使用多线程比使用多进程要更有优势,但是线程并不是越多越好,如何控制线程的个数呢?

  • 文件 IO 操作: File IO does not have a high CPU usage rate, so CPU time slices can be multiplexed in time division, the number of threads = 2 * number of CPU cores (highest efficiency)

  • 处理复杂的算法(Mainly because the CPU performs calculations, and the pressure is high),线程的个数 = CPU 的核心数 (效率最高)

2. Create a thread

Note: 我们创建的线程是子线程,而不是主线程, because the main thread exists by default, and because a program is started, a process is obtained, and the thread creation function is called in this process, and the child thread is obtained, and the original process degenerates into a child thread 主线程. Therefore, it degenerates from a single-process application program to a multi-thread application program, but in the program 主线程退出之后,地址空间也就销毁了, the sub-thread is destroyed if the address space does not exist.

Each thread has a unique 线程 IDID type pthread_t, which is an unsigned long integer. If you want to get the thread ID of the current thread, you can call the following function:

pthread_t pthread_self(void);	// 返回当前线程的线程ID

Call the thread creation function in a process to get a child thread, which is different from a process.需要给每一个创建出的线程指定一个处理函数,否则这个线程无法工作

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine) (void *), void *arg);
// Compile and link with -pthread, 线程库的名字叫pthread, 全名: libpthread.so libptread.a

parameter:

  • thread: 传出参数, is an unsigned long integer. If the thread is created successfully, the thread ID will be written into the memory pointed to by this pointer.

  • attr: 线程的属性, generally use the default attribute, write NULL

  • start_routine: 函数指针, the processing action of the created sub-thread, that is, the function is executed in the sub-thread.

  • arg:Passed as an actual parameter to the function pointed to by the start_routine pointer

  • 返回值: If the thread is successfully created, it will return 0; if it fails, it will return the corresponding error number

2.1 Create thread

线程函数与规定的函数指针类型The following is a sample code for creating a thread. Be sure to write consistently during the creation process : void *(*start_routine) (void *):

// pthread_create.c 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 子线程的处理代码
void* working(void* arg)
{
    
    
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }
    
    // 休息, 休息一会儿...
    // sleep(1);
    
    return 0;
}

程序的执行逻辑

  • When the program is started, the entry point of the program must be the main function, and then in the main thread, the pthread_createsub-thread is created. After the sub-thread is created, it will execute its 任务函数working, from the first line of the working function, down. After execution, the child thread exits.
  • The execution of the main thread will be executed downward from the main function, including creating sub-threads, and then executing a for loop until the execution is completed. But the main thread will not execute the task function of the child thread, and the task function is processed in the child thread.

Compile the test program, you will see the following error message:

$ gcc pthread_create.c 
/tmp/cctkubA6.o: In function `main':
pthread_create.c:(.text+0x7f): undefined reference to `pthread_create'
collect2: error: ld returned 1 exit status

错误原因是因为编译器链接不到线程库文件(动态库), it needs to be specified by parameters when compiling, the name of the dynamic library libpthread.sois -l, and the final form should be written as: -lpthread (there can be spaces between parameters and parameter values) according to the rules. The correct compilation command is:

# pthread_create 函数的定义在某一个库中, 编译的时候需要加库名 pthread
$ gcc pthread_create.c -lpthread
$ ./a.out 
子线程创建成功, 线程ID: 139712560109312
我是主线程, 线程ID: 139712568477440
i = 0
i = 1
i = 2

In the log output printed 为什么子线程处理函数没有执行完毕呢(only saw part of the log output from the child thread)?
主线程一直在运行,执行期间创建出了子线程,说明主线程有 CPU 时间片,在这个时间片内将代码执行完毕了,主线程就退出了. After the child thread is created, it needs to grab the cpu time slice, and it cannot run if it cannot be grabbed 如果主线程退出了, 虚拟地址空间就被释放了, 子线程就一并被销毁了. But if a child thread exits, the main thread is still running, and the virtual address space still exists.

The conclusion obtained: 在没有人为干预的情况下,虚拟地址空间的生命周期和主线程是一样的,与子线程无关。 目前的解决方案:让子线程执行完毕,主线程再退出,可以在主线程中添加挂起函数 sleep();the main thread gives sleepup the CPU resources when it is executed. At this time, the child thread grabs the CPU time slice and executes downward. After the execution is completed, the main thread wakes up, and then the main thread grabs the time slice and continues to Next execute.

3. Thread exits

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程), we can call the thread exit function in the thread library, as long as the function is called, the current thread will exit immediately, and it will not affect the normal operation of other threads. It can be used in sub-threads or main threads.

In fact 这个函数主要是在主线程中使用, because there are multiple threads in one address space, whether 主线程和多个子线程the main thread exits or not does not affect the execution of the main thread, because the sub-thread exits the address space and still exists. But when the main thread exits, the virtual address space is released as a whole, and the child threads are also released together.

So say 如果想要让主线程退出,子线程依然可以继续运行,我们就需要调用线程退出函数,让主线程让主线程自己退出去,不要把虚拟地址空间释放掉. After the sub-threads exit one by one, the program execution is completed, and the virtual address space is reclaimed by the operating system.

线程退出函数

#include <pthread.h>
void pthread_exit(void *retval);
  • Parameter: The data carried when the thread exits, the main thread of the current sub-thread will get the data. If you do not need to use it, specify NULL; as for retvalhow to receive the passed out, we need to 线程回收函数use it together, which will be introduced below

The following is a sample code for thread exit, which can be called in any thread where needed:

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

// 子线程的处理代码
void* working(void* arg)
{
    
    
    sleep(1);
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
   for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 主线程调用退出函数退出, 地址空间不会被释放
    pthread_exit(NULL);
    
    return 0;
}
  • If it is used pthread_exit, although the main thread exits, the child thread can still execute regardless of the virtual address space.

4. Thread recycling

4.1 Thread function

This thread recycling function is called pthread_join, used to reclaim sub-thread resources, 是由主线程来回收子线程资源的. The main thread has an obligation to reclaim the sub-thread resources when the sub-thread ends. 但并不是所有的资源都有主线程回收,回收的是内核中的资源, because these multiple threads share the same virtual address space, for data such as heap area, stack area, code segment, etc., it will be released automatically after the child thread ends. The main thread mainly helps the child thread release the resources in the kernel. This The child thread cannot release itself.

pthread_join(), this function 是一个阻塞函数, if there are sub-threads running, calling this function will block, and the sub-thread exits the function to unblock and recycle resources 函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收.

In addition, the thread recycling function can also obtain the data passed when the sub-thread exits. Whoever recycles the sub-thread can get the data, that is, the main thread can get the recycled data. The function prototype is as follows:

#include <pthread.h>
// 这是一个阻塞函数, 子线程在运行这个函数就阻塞
// 子线程退出, 函数解除阻塞, 回收对应的子线程资源, 类似于回收进程使用的函数 wait()
int pthread_join(pthread_t thread, void **retval);

parameter:

  • thread: the thread ID of the child thread to be recycled

  • retval: Second-level pointer, pointing to the address of the first-level pointer, is an outgoing parameter, this address stores the data passed by pthread_exit (), if this parameter is not needed, it can be specified as NULL

  • Return value: Thread recovery returns 0 successfully, recovery fails and returns an error number.

4.2 Recycling sub-thread data

When the sub-thread exits, pthread_exit() the parameter can be used to transfer the data, and when the sub-thread is recycled, the phread_join() second parameter can be passed to receive the data passed by the sub-thread. There are many ways to process received data, here are a few:

4.2.1 Using the sub-thread stack

Through the function pthread_exit(void retval); we can know that when the sub-thread exits, the data needs to be recorded in a piece of memory, and the address of the memory where the data is stored is transmitted through the parameter, not the specific data, because the parameter is void Type, all this universal pointer can point to any type of memory address. Let’s look at the first method first, saving the sub-thread’s exit data in the sub-thread’s own stack area:

// pthread_join.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>

// 定义结构
struct Persion
{
    
    
    int id;
    char name[36];
    int age;
};

// 子线程的处理代码
void* working(void* arg)
{
    
    
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
        if(i == 6)
        {
    
    
            struct Persion p;
            p.age  =12;
            strcpy(p.name, "tom");
            p.id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(&p);
        }
    }
    return NULL;	// 代码执行不到这个位置就退出了
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    struct Persion* pp = (struct Persion*)ptr;
    printf("子线程返回数据: name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);
    printf("子线程资源被成功回收...\n");
    
    return 0;
}

  • The sub-thread passes through pthread_exit(&p);and passes out the structure data p, and the sub-thread passes through pthread_jointo recycle tidthe corresponding sub-thread, and receives the structure data passed by the sub-thread through the second parameter
  • pthread_joinpthread_joinIt is a blocking function. The main thread will wait for the sub-thread to finish executing until this line is executed. After the sub-thread is executed, it will be recycled, and the address of the passed data will be executed through the second parameter; then, the main thread will be passed by the sub- 对void* 数据类型的数据进行转换thread The data came out.

Compile and execute the test program:

# 编译代码
$ gcc pthread_join.c -lpthread
# 执行程序
$ ./a.out 
子线程创建成功, 线程ID: 140652794640128
我是主线程, 线程ID: 140652803008256
i = 0
i = 1
i = 2
我是子线程, 线程ID: 140652794640128
child == i: = 0
child == i: = 1
child == i: = 2
child == i: = 3
child == i: = 4
child == i: = 5
child == i: = 6
子线程返回数据: name: , age: 0, id: 0
子线程资源被成功回收...

From the printed log, it can be found that the data information returned by the child thread is not obtained in the main thread. The specific reason is as follows:

  • 如果多个线程共用同一个虚拟地址空间,每个线程在栈区都有一块属于自己的内存,相当于栈区被这几个线程平分了,当线程退出,线程在栈区的内存也就被回收了,因此随着子线程的退出,写入到栈区的数据也就被释放了。
  • struct Persion pThe temporary variable created by the sub-thread is stored in the stack memory. When the sub-thread exits, the stack memory is automatically released, and the data in the stack memory is released. At this time, the temporary variable pno longer exists, and if you access this memory space again, there will be no data.
  • How to ensure that the recovered data can be obtained? Then it must be guaranteed struct Persion pnot to be released, so this variable can be defined as a global variable, or a static variable

4.2.2 Using global variables

Threads located in the same virtual address space, 虽然不能共享栈区数据,但是可以共享全局数据区和堆区数据so outgoing data can be saved when the child thread exits 储到全局变量、静态变量或者堆内存中. In the following example the data is stored in a global variable:

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

// 定义结构
struct Persion
{
    
    
    int id;
    char name[36];
    int age;
};

struct Persion p;	// 定义全局变量

// 子线程的处理代码
void* working(void* arg)
{
    
    
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
        if(i == 6)
        {
    
    
            // 使用全局变量
            p.age  =12;
            strcpy(p.name, "tom");
            p.id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(&p);
        }
    }
    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    struct Persion* pp = (struct Persion*)ptr;
    printf("name: %s, age: %d, id: %d\n", pp->name, pp->age, pp->id);
    printf("子线程资源被成功回收...\n");
    
    return 0;
}

4.2.3 Using the main thread stack

虽然每个线程都有属于自己的栈区空间,但是位于同一个地址空间的多个线程是可以相互访问对方的栈空间上的数据的. In many cases 主线程中回收子线程资源,所以主线程一般都是最后退出, it is necessary to save the data returned by the child thread to the stack memory of the main thread in the following program for this reason:

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

// 定义结构
struct Persion
{
    
    
    int id;
    char name[36];
    int age;
};


// 子线程的处理代码
void* working(void* arg)
{
    
    
    struct Persion* p = (struct Persion*)arg;
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
        if(i == 6)
        {
    
    
            // 使用主线程的栈内存
            p->age  =12;
            strcpy(p->name, "tom");
            p->id = 100;
            // 该函数的参数将这个地址传递给了主线程的pthread_join()
            pthread_exit(p);
        }
    }
    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;

    struct Persion p;
    // 主线程的栈内存传递给子线程
    pthread_create(&tid, NULL, working, &p);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 阻塞等待子线程退出
    void* ptr = NULL;
    // ptr是一个传出参数, 在函数内部让这个指针指向一块有效内存
    // 这个内存地址就是pthread_exit() 参数指向的内存
    pthread_join(tid, &ptr);
    // 打印信息
    printf("name: %s, age: %d, id: %d\n", p.name, p.age, p.id);
    printf("子线程资源被成功回收...\n");
    
    return 0;
}
  • Therefore, it will struct Persion pbe defined in the main thread, and then pass parameters to the sub-thread through pthread_createthe last parameter p, so that the sub-thread function can receive pthe variable.
  • Then the child thread modifies the value of p, and pthread_exitpasses the data out. When it executes to pthread_join, it waits for the child thread to finish executing and consumes the stack memory, becausep是在主线程中的,此时主线程并没有销毁,因此主线程中依然可以保留p的数据

In the above program, pthread_create() is called to create a sub-thread, and the address of the stack space variable p in the main thread is passed to the sub-thread, and the data to be transferred is written into this memory in the sub-thread. That is to say, in the main() function of the program, the data transmitted by the child thread can be read through the pointer variable ptr or the structure variable p.

5. Thread separation

  • pthread_detachThread separation function. By default, the main thread and the sub-thread are related, that is, the resources of the sub-thread will be released when the main thread exits. If the main thread exits after the thread is separated, the corresponding sub-thread will not be released. of that resource.

  • Because in some cases, if the thread is not separated, you need to call it if you want to reclaim the sub-thread resources join, and jointhe main thread will be blocked as soon as the function is called. Joining we want the main thread to perform some other things, but also want him to recycle the sub-thread resources, you just call join and block there, waiting for the sub-thread to finish executing. So you can use to pthread_detachseparate the main thread and the child thread

  • Although the sub-thread is separated from the main thread, when the main thread exits, the address space is released, and the sub-thread cannot run.

#include <pthread.h>
// 参数就子线程的线程ID, 主线程就可以和这个子线程分离了
int pthread_detach(pthread_t thread);

The thread detachment function pthread_detach() is provided for us in the thread library function. After calling this function, the specified sub-thread can be separated from the main thread. When the sub-thread exits, the kernel resources it occupies will be taken over by other processes in the system and recycled. After the thread is separated, using pthread_join() in the main thread will not reclaim the sub-thread resources.

In the following code, a child thread is created in the main thread, and the thread separation function is called to realize the separation of the main thread and the child thread:

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

// 子线程的处理代码
void* working(void* arg)
{
    
    
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf("child == i: = %d\n", i);
    }
    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 设置子线程和主线程分离
    pthread_detach(tid);

    // 让主线程自己退出即可
    pthread_exit(NULL);
    
    return 0;
}
  • It is necessary to pass in sub-threads idand specify which sub-thread is separated from the main thread pthread_detach(tid), so that the sub-threads and the main thread can execute their own tasks without joinblocking.
  • In order to make the main thread exit without affecting the separation of sub-threads, you can callpthread_exit(NULL)
  • The use pthread_detachis equivalent to reducing the burden on the main thread, so that the resources of the sub-threads are reclaimed by the system, and there is no need for sub-threads to recycle.

6. Other thread functions

6.1 Thread cancellation

Thread cancellation means killing a thread in another thread under certain circumstances. Using this function to kill a thread requires two steps:

  • Call the thread cancellation function pthread_cancel in thread A, and specify to kill thread B. At this time, thread B cannot die
  • In thread B, a system call (switching from user area to kernel area) is called once 系统函数, otherwise thread B can run forever. System calls are ubiquitous. For example, executing printf is equivalent to calling system functions indirectly.io
#include <pthread.h>
// 参数是子线程的线程ID
int pthread_cancel(pthread_t thread);
  • parameter:要杀死的线程的线程 ID
  • Return value: The function returns 0 successfully, and returns a non-zero error number if the call fails.

In the sample code below, the main thread calls the thread cancel function, as long as the system call is made in the child thread, when the child thread executes to this position, it will hang up.

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

// 子线程的处理代码
void* working(void* arg)
{
    
    
    int j=0;
    for(int i=0; i<9; ++i)
    {
    
    
        j++;
    }
    // 这个函数会调用系统函数, 因此这是个间接的系统调用
    printf("我是子线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<9; ++i)
    {
    
    
        printf(" child i: %d\n", i); //printf 进行了系统调用
    }

    return NULL;
}

int main()
{
    
    
    // 1. 创建一个子线程
    pthread_t tid;
    pthread_create(&tid, NULL, working, NULL);

    printf("子线程创建成功, 线程ID: %ld\n", tid);
    // 2. 子线程不会执行下边的代码, 主线程执行
    printf("我是主线程, 线程ID: %ld\n", pthread_self());
    for(int i=0; i<3; ++i)
    {
    
    
        printf("i = %d\n", i);
    }

    // 杀死子线程, 如果子线程中做系统调用, 子线程就结束了
    pthread_cancel(tid);

    // 让主线程自己退出即可
    pthread_exit(NULL);
    
    return 0;
}

6.2 Thread ID Comparison

In Linux, the thread ID is essentially an unsigned long integer, so you can directly use the comparison operator to compare the IDs of two threads, but the thread library can be used across platforms. On some platforms, pthread_t may not be a simple integer. In this case, the comparison function must be used to compare the IDs of two threads. The function prototype is as follows:

#include <pthread.h>
int pthread_equal(pthread_t t1, pthread_t t2);
  • Parameters: t1 and t2 are the thread IDs of the threads to be compared
  • Return value: If the two thread IDs are equal, return a non-zero value, and return 0 if they are not equal

Guess you like

Origin blog.csdn.net/weixin_38346042/article/details/131612746