1. 协程是轻量级线程,轻量级表现在哪里?⭐⭐⭐⭐⭐

  2. 说说线程间通信的方式有哪些?⭐⭐⭐⭐⭐

  3. 说说线程同步方式有哪些?⭐⭐⭐⭐⭐

  4. 有了进程,为什么还要有线程?⭐⭐⭐⭐⭐

  5. 单核机器上写多线程程序,是否要考虑加锁,为什么?⭐⭐⭐⭐⭐

  6. 说说多线程和多进程的不同?⭐⭐⭐⭐⭐

  7. 简述互斥锁的机制,互斥锁与读写的区别?⭐⭐⭐⭐⭐

  8. 说说线程池的设计思路,线程池中线程的数量由什么确定?⭐⭐⭐

  9. 进程和线程相比,为什么慢?⭐⭐⭐⭐⭐

=========================================================================================================

  • 本专栏适合于C/C++已经入门的学生或人士,有一定的编程基础。
  • 本专栏适合于互联网C++软件开发、嵌入式软件求职的学生或人士。
  • 本专栏针对面试题答案进行了优化,尽量做到好记、言简意赅。这才是一份面试题总结的正确打开方式。这样才方便背诵
  • 针对于非科班同学,建议学习本人专刊文章《蒋豆芽的秋招打怪之旅》,该专刊文章对每一个知识点进行了详细解析。
  • 如专栏内容有错漏,欢迎在评论区指出或私聊我更改,一起学习,共同进步。
  • 相信大家都有着高尚的灵魂,请尊重我的知识产权,未经允许严禁各类机构和个人转载、传阅本专栏的内容。

=========================================================================================================

  1. 协程是轻量级线程,轻量级表现在哪里?⭐⭐⭐⭐⭐

    1. 协程调用跟切换比线程效率高:协程执行效率极高。协程不需要多线程的锁机制,可以不加锁的访问全局变量,所以上下文的切换非常快。

    2. 协程占用内存少:执行协程只需要极少的栈内存(大概是4~5KB),而默认情况下,线程栈的大小为1MB。

    3. 切换开销更少:协程直接操作栈基本没有内核切换的开销,所以切换开销比线程少。

  2. 说说线程间通信的方式有哪些?⭐⭐⭐⭐⭐

    线程间的通信方式包括互斥量、信号量、条件变量、读写锁

    1. 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
    2. 信号量:计数器,允许多个线程同时访问同一个资源。
    3. 条件变量:通过条件变量通知操作的方式来保持多线程同步。
    4. 读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。
  3. 说说线程同步方式有哪些?⭐⭐⭐⭐⭐

    线程间的同步方式包括互斥锁、信号量、条件变量、读写锁

    1. 互斥锁:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
    2. 信号量:计数器,允许多个线程同时访问同一个资源。
    3. 条件变量:通过条件变量通知操作的方式来保持多线程同步。
    4. 读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。
  4. 有了进程,为什么还要有线程?⭐⭐⭐⭐⭐

    1. 原因

      进程在早期的多任务操作系统中是基本的执行单元。每次进程切换,都要先保存进程资源然后再恢复,这称为上下文切换。但是进程频繁切换将引起额外开销,从而严重影响系统的性能。为了减少进程切换的开销,人们把两个任务放到一个进程中,每个任务用一个更小粒度的执行单元来实现并发执行,这就是线程

    2. 线程与进程对比

      (1)**进程间的信息难以共享。**由于除去只读代码段外,父子进程并未共享内存,因此必须采用一些进程间通信方式,在进程间进行信息交换。

      多个线程共享进程的内存,如代码段、数据段、扩展段,线程间进行信息交换十分方便。

      (2)调用 fork() 来创建进程的代价相对较高,即便利用写时复制技术,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性,这意味着 fork() 调用在时间上的开销依然不菲。

      **但创建线程比创建进程通常要快 10 倍甚至更多。**线程间是共享虚拟地址空间的,无需采用写时复制来复制内存,也无需复制页表。

  5. 单核机器上写多线程程序,是否要考虑加锁,为什么?⭐⭐⭐⭐⭐

    在单核机器上写多线程程序,仍然需要线程锁。

    原因:因为线程锁通常用来实现线程的同步和通信。在单核机器上的多线程程序,仍然存在线程同步的问题。因为在抢占式操作系统中,通常为每个线程分配一个时间片,当某个线程时间片耗尽时,操作系统会将其挂起,然后运行另一个线程。如果这两个线程共享某些数据,不使用线程锁的前提下,可能会导致共享数据修改引起冲突。

  6. 说说多线程和多进程的不同?⭐⭐⭐⭐⭐

    (1)一个线程从属于一个进程;一个进程可以包含多个线程。

    (2)一个线程意外死亡,可能导致进程挂掉,多线程也可能挂掉;一个进程挂掉,不会影响其他进程,多进程稳定。

    (3)进程系统开销显著大于线程开销;线程需要的系统资源更少。

    (4)多个进程在执行时拥有各自独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。

    (5)多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。

    (6)通信方式不一样。

    (7)多进程适应于多核、多机分布;多线程适用于多核

  7. 简述互斥锁的机制,互斥锁与读写的区别?⭐⭐⭐⭐⭐

    1. 互斥锁机制:mutex,用于保证在任何时刻,都只能有一个线程访问该对象。当获取锁操作失败时,线程会进入阻塞,等待锁释放。

    2. 互斥锁和读写锁

      (1) 读写锁区分读者和写者,而互斥锁不区分

      (2)互斥锁同一时间只允许一个线程访问该对象,无论读写;读写锁同一时间内只允许一个写者,但是允许多个读者同时读对象。

  8. 说说线程池的设计思路,线程池中线程的数量由什么确定?⭐⭐⭐

    1. 设计思路

      实现线程池有以下几个步骤: (1)设置一个生产者消费者队列,作为临界资源。

      (2)初始化n个线程,并让其运行起来,加锁去队列里取任务运行

      (3)当任务队列为空时,所有线程阻塞。

      (4)当生产者队列来了一个任务后,先对队列加锁,把任务挂到队列上,然后使用条件变量去通知阻塞中的一个线程来处理。

    2. 线程池中线程数量

      线程数量和哪些因素有关:CPU,IO、并行、并发

      如果是CPU密集型应用,则线程池大小设置为:CPU数目+1
      如果是IO密集型应用,则线程池大小设置为:2*CPU数目+1
      最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
      

      所以线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

    3. 为什么要创建线程池

      创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。同时线程池也是为了提升系统效率。

    4. 线程池的核心线程与普通线程:

      任务队列可以存放100个任务,此时为空,线程池里有10个核心线程,若突然来了10个任务,那么刚好10个核心线程直接处理;若又来了90个任务,此时核心线程来不及处理,那么有80个任务先入队列,再创建核心线程处理任务;若又来了120个任务,此时任务队列已满,不得已,就得创建20个普通线程来处理多余的任务。 以上是线程池的工作流程。

  9. 进程和线程相比,为什么慢?⭐⭐⭐⭐⭐

    1. 进程系统开销显著大于线程开销;线程需要的系统资源更少。
    2. 进程切换开销比线程大。多进程切换时需要刷新TLB并获取新的地址空间,然后切换硬件上下文和内核栈;多线程切换时只需要切换硬件上下文和内核栈。
    3. 进程通信比线程通信开销大。进程通信需要借助管道、队列、共享内存,需要额外申请空间,通信繁琐;而线程共享进程的内存,如代码段、数据段、扩展段,通信快捷简单,同步开销更小。

##豆芽点评 线程和进程很重要,高频考点。