【面试八股总结】线程基本概念,线程、进程和协程区别,线程实现

一、什么是线程?

线程是"轻量级进程",是进程中的⼀个实体,是程序执⾏的最小单元,也是被系统独立调度和分配的基本单位。

线程是进程当中的⼀条执行流程 ,同⼀个进程内多个线程之间可以共享代码段、数据段、打开的文件等资源,但每个线程各自都有⼀套独立的寄存器和栈,这样可以确保线程的控制流是相对独立的。

二、线程的优缺点

线程的优点:

  • 一个进程中可以同时存在多个线程;
  • 各个线程之间可以并发执行;
  • 各个线程之间可以共享地址空间和文件等资源;

线程的缺点:

  • 当进程中的一个线程崩溃时,会导致其所属进程的所有线程崩溃

三、进程、线程和协程区别

|------|-------------------------------------------|-------------------------|------------------------------------------|
| | 进程 | 线程 | 协程 |
| 定义 | 资源分配和拥有的基本单位 | 程序执行的基本单位 | 用户态的轻量级线程 |
| 切换情况 | 保存和设置进程CPU环境(栈、寄存器、页表和文件句柄) | 保存和设置程序计数器、少量寄存器和栈 | 先将寄存器上下文和栈保存,等切换回来的时候再进行恢复 |
| 切换者 | 操作系统 | 操作系统 | 用户 |
| 切换过程 | 用户态->核心态->用户态 | 用户态->核心态->用户态 | 用户态 |
| 调用栈 | 内核栈 | 内核栈 | 用户栈 |
| 拥有资源 | CPU资源、内存资源、文件资源和句柄等 | 程序计数器、寄存器、栈和状态字 | 拥有自己的寄存器上下文和栈 |
| 并发性 | 不同进程之间切换实现并发,各自占有CPU实现并行 | 一个进程内部的多个线程并发执行 | 同一时间只能执行一个协程,其他协程处于休眠状态,适合对任务进行分时处理 |
| 系统开销 | 切换虚拟机地址空间,切换内核栈和硬件上下文,开销大 | 切换时只需保存和设置很少量的寄存器内容,开销小 | 直接操作栈,基本没有内核切换开销,可以不加锁的访问全局变量,上下文切换速度非常快 |
| 通信方面 | 需要借助操作系统(管道、消息队列、共享内存、内存映射、信号量、信号、Socket) | 直接读写进程数据段(eg.全局变量)进行通信 | 共享内存、消息队列 |

四、线程实现

1. 线程创建和结束

  • 一般情况下,main函数所在的线程我们称之为主线程(main线程),其余创建的线程称之为子线程。 程序中默认只有一个线程,调用pthread_create()函数产生新的线程。
cpp 复制代码
​​​​​​// ​创建线程 
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

        - 功能:创建一个子线程
        - 参数:
            - thread:传出参数,线程创建成功后,子线程的线程ID被写到该变量中。
            - attr : 设置线程的属性,一般使用默认值,NULL
            - start_routine : 函数指针,这个函数是子线程需要处理的逻辑代码
            - arg : 给第三个参数使用,传参
        - 返回值:
            成功:0
            失败:返回错误号。这个错误号和之前errno不太一样。
            获取错误号的信息:  char * strerror(int errnum);
  • 获得线程id :pthread_self
cpp 复制代码
pthread_t pthread_self(void);
  • 等待线程结束:pthread_join,主线程调⽤,等待子线程退出并回收其资源,类似于进程中wait/waitpid回收僵尸进程,调用 pthread_join的线程会被阻塞
cpp 复制代码
int pthread_join(pthread_t thread, void **retval);
        - 功能:和一个已经终止的线程进行连接
               回收子线程的资源
        - 特点:这个函数是阻塞函数,调用一次只能回收一个子线程
               一般在主线程中使用
        - 参数:
            - thread:需要回收的子线程的ID
            - retval: 接收子线程退出时的返回值
        - 返回值:
            0 : 成功
            非0 : 失败,返回的错误号
  • 结束线程: 子线程执行,用于结束当前线程并通过retval传递返回值,该返回值可通过pthread_join获得
cpp 复制代码
void pthread_exit(void *retval);
        功能:终止一个线程,在哪个线程中调用,就表示终止哪个线程
        参数:
            retval:需要传递一个指针,作为一个返回值,可以在pthread_join()中获取到。
  • 分离线程:主线程、子线程均可调⽤。主线程中pthread_detach(tid),子线程中 pthread_detach(pthread_self()),调⽤后和主线程分离,子线程结束时自己立即回收资源
cpp 复制代码
 int pthread_detach(pthread_t thread);
        - 功能:分离一个线程。被分离的线程在终止的时候,会自动释放资源返回给系统。
              1. 不能多次分离,会产生不可预料的行为。
              2. 不能去连接一个已经分离的线程,会报错。
        - 参数:需要分离的线程的ID
        - 返回值:
            成功:0
            失败:返回错误号

2. 线程属性

线程属性对象类型为pthread_attr_t,结构体定义如下:

cpp 复制代码
typedef struct{
    int detachstate;    // 线程分离的状态
    int schedpolicy;    // 线程调度策略
    struct sched_param schedparam;  // 线程的调度参数
    int inheritsched;    // 线程的继承性
    int scope;    //线程的作用域
       
     //以下为线程栈的设置
    size_t guardsize;    //线程栈末尾警戒缓冲大小
    int stackaddr_set;    // 线程的栈设置
    void * stackaddr;// 线程栈的位置
    size_t stacksize;//线程栈大小
}pthread_attr_t;
cpp 复制代码
设置线程属性相关函数:

    int pthread_attr_init(pthread_attr_t *attr);
        - 初始化线程属性变量

    int pthread_attr_destroy(pthread_attr_t *attr);
        - 释放线程属性的资源

    int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
        - 获取线程分离的状态属性

    int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
        - 设置线程分离的状态属性
相关推荐
Estrella162 小时前
20分钟教会你 “变量提升、调用栈和作用域链”
前端·javascript·面试
拉丁解牛说技术4 小时前
JAVA并发编程系列(11)线程池底层原理架构剖析
java·面试
打鱼又晒网7 小时前
linux信号| 学习信号三步走 | 学习信号需要打通哪些知识脉络?
linux·服务器·后端·操作系统
孟健9 小时前
【校招】字节跳动Parallel前端团队招人啦~
面试·aigc·招聘
影子落人间9 小时前
Spring面试题-Spring是怎么解决循环依赖的?
java·spring·面试
每天的积累11 小时前
面试知识点总结篇一
单片机·嵌入式硬件·面试·应用层
水寒之11 小时前
简历技能面试问答
面试·职场和发展
鱼跃鹰飞12 小时前
Leetcode面试经典150题-172.阶乘后的零
java·算法·leetcode·面试·职场和发展