【Linux】C语言中多线程的创建、退出、回收、分离

概述

线程是轻量级的进程(LWP:light weight process),在 Linux 环境下线程的本质仍是进程。在计算机上运行的程序是一组指令及指令参数的组合,指令按照既定的逻辑控制计算机运行。操作系统会以进程为单位,分配系统资源,可以这样理解,进程是资源分配的最小单位,线程是操作系统调度执行的最小单位

在Linux系统下,线程的创建和管理是通过pthread库实现的。pthread是POSIX线程库,提供了创建、终止、同步和通信线程的函数和数据结构。

创建线程

线程函数

每一个线程都有一个唯一的线程 ID,ID 类型为 pthread_t,这个 ID 是一个无符号长整形数,如果想要得到当前线程的线程 ID,可以调用如下函数:

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

在Linux系统下,可以使用pthread_create函数来创建线程。pthread_create函数的原型如下:

#include <pthread.h>

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

  • thread:指向pthread_t类型的指针,用于存储新创建线程的标识符。在成功创建线程后,该指针将被填充为一个唯一的标识符,用于后续对线程的引用。
  • attr:指向pthread_attr_t类型的指针,用于指定线程的属性。线程属性对象可以控制线程的各种行为,例如线程的调度策略、栈大小、分离状态等。如果不需要对线程属性进行特殊设置,可以传入NULL,使用默认属性。
  • start_routine:指向线程函数的指针,该函数是线程的入口点。线程函数是线程的实际执行体,当线程被创建时,将从该函数开始执行。函数的返回类型必须为void*,并接受一个void*类型的参数。线程函数可以执行任意操作,包括计算、访问共享资源等。
  • arg:传递给线程函数的参数,类型为void*。可以将任意类型的数据传递给线程函数,只需将其转换为void*类型。在线程函数内部,可以使用适当的类型转换将参数恢复为原始类型。

返回值:线程创建成功返回 0,创建失败返回对应的错误号

在编写多线程程序的时候,如果想要让线程退出,但是不会导致虚拟地址空间的释放(针对于主线程),我们就可以调用线程库中的线程退出函数,只要调用该函数当前线程就马上退出了,并且不会影响到其他线程的正常运行,不管是在子线程或者主线程中都可以使用。

void pthread_exit(void *retval);

参数 retval 是一个指向任意类型的指针,表示线程的退出状态。线程的退出状态可以用于与其他线程进行通信或传递结果。

pthread_self() 是一个 POSIX 线程库函数,用于获取当前线程的线程 ID。

pthread_t pthread_self(void);

线程创建

注意:线程函数可以接受一个指向任意类型的参数,并且返回一个指向任意类型的指针。
首先使用vim pthread_create.c创建c语言文件。
键入代码:

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

void *callback(void* arg)
{
    
    
        for(int i=0;i<5;++i)
                {
    
    
                        printf("子线程:i=%d\n",i);

                }
        printf("子线程:%ld\n",pthread_self());
        return NULL;
}

int main()
{
    
    
        pthread_t tid;
        pthread_create(&tid,NULL,callback,NULL);
        for(int i=0;i<5;++i)
        {
    
    
                printf("主线程:i=%d\n",i);
        }
        printf("主线程:%ld\n",pthread_self());
        pthread_exit(NULL);
        return 0;
}

保存文件后使用g++ pthread_create.c -lpthread -o app命令进行编译。
该命令用于编译名为 pthread_create.c 的源代码文件,并链接 pthread 库生成可执行文件 app。

g++:是 GNU 编译器集合中的 C++ 编译器。
pthread_create.c:是要编译的源代码文件的名称。
-lpthread:表示链接 pthread 库,这是 POSIX 线程库。
-o app2:指定生成的可执行文件的名称为 app2

在这里插入图片描述
执行./app,结果如下所示
在这里插入图片描述

线程回收

在线程编程中,线程回收是指等待线程执行结束并收回相关资源。在 POSIX 线程库中,可以使用 pthread_join 函数来实现线程的回收。pthread_join 函数的原型如下:

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

其中,thread 参数是要回收的线程标识符,retval参数用于接收线程的返回值。函数的返回值表示回收线程的执行状态,如果成功回收线程,则返回 0,否则返回一个非零值,表示出现了错误。

当调用 pthread_join 函数时,当前线程会被阻塞,直到指定的线程执行结束。一旦线程执行结束,它的资源将被回收,并可以通过 retval 参数获取线程的返回值。

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
struct persion
{
    
    
int num;
int age;
};
void *callback(void* arg)
{
    
    
        for(int i=0;i<5;++i)
                {
    
    
                        printf("子线程:i=%d\n",i);

                }
        printf("子线程:%ld\n",pthread_self());
        struct persion* t = (struct persion*)arg;
        t->num=100;
        t->age=5;
        pthread_exit(&t);
        return NULL;
}

int main()
{
    
    
        pthread_t tid;
        struct persion t;
        pthread_create(&tid,NULL,callback,&t);
        for(int i=0;i<5;++i)
        {
    
    
                printf("主线程:i=%d\n",i);
        }
        printf("主线程:%ld\n",pthread_self());
        void *ptr;
        pthread_join(tid,&ptr);
        struct persion *pt=(struct persion*)ptr;
        printf("num:%d,age:%d\n",t.num,t.age);
        pthread_exit(NULL);
        return 0;
}

pthread_join其实是个阻塞函数,如果还有子线程在运行,调用该函数就会阻塞,子线程退出函数解除阻塞进行资源的回收,函数被调用一次,只能回收一个子线程,如果有多个子线程则需要循环进行回收。

ptr 用于接收子线程的返回值,pt 通过强制类型转换指向子线程的返回值,而 t 是主线程中的一个独立对象

结果如下所示:
在这里插入图片描述

线程分离

线程的分离是指将线程设置为分离状态,使其在退出时自动释放资源,不需要显式地调用 pthread_join 函数来等待线程的结束。

在某些情况下,程序中的主线程有属于自己的业务处理流程,如果让主线程负责子线程的资源回收,调用 pthread_join() 只要子线程不退出主线程就会一直被阻塞,主要线程的任务也就不能被执行了。

线程分离之后在主线程中使用 pthread_join() 就回收不到子线程资源了。

        for(int i=0;i<5;++i)
                {
    
    
                        printf("子线程:i=%d\n",i);

                }
        printf("子线程:%ld\n",pthread_self());
        struct persion* t = (struct persion*)arg;
        t->num=100;
        t->age=5;
        pthread_exit(&t);
        return NULL;
}

int main()
{
    
    
        pthread_t tid;
        struct persion t;
        pthread_create(&tid,NULL,callback,&t);
        for(int i=0;i<5;++i)
        {
    
    
                printf("主线程:i=%d\n",i);
        }
        printf("主线程:%ld\n",pthread_self());
        pthread_detach(tid);
        pthread_exit(NULL);

        return 0;
}

线程分离后,子线程执行完毕后被系统内核回收了,且主线程退出后不会影响子线程的执行。

猜你喜欢

转载自blog.csdn.net/qq_44878985/article/details/131270651