Linux 多线程(线程概念/特点/优缺点/与进程比较)

目录

线程

线程间数据的独有与共享(同组线程)

线程的优点--("轻"+多任务并发/并行处理的优势(对比于进程))

线程的缺点

多线程与多进程的比较

Linux 多线程(线程控制(创建/终止/等待/分离))


什么是线程?

说到线程, 离不开的概念就是进程 . 也离不开计算机或操作系统的发展过程. 简单在另一篇博客写了一下.

具体进程和线程的引入来历, 戳链接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/104556533

简单来说, 进程让操作系统的并发成为可能性, 线程让进程内部的并发成为可能.

那么在Linux下, 再来看线程具体是什么

线程

  • 线程包含在进程之中, 是进程的实际运作单位. 进程内部的一个执行路线分支(或者说是单一顺序的控制流)就叫做线程
     
  • 线程是操作系统能够进行运算调度的最小单位
     
  • 每个进程至少都有一个执行线程, 一个进程中可以并发多个线程, 每条线程执行不同的任务.
     
  • 线程在进程内部运行, 本质是在进程地址空间内运行

在Linux中具体实现

  • 线程是轻量级进程 LWP(Light Weight Process)
     
  • 在Linux下没有为了实现线程而创建新的数据结构, 还是用原来进程的PCB来描述线程, 在Linux下就是task_struct结构体
     
  • 进程的PCB有独立的地址空间
     
  • 线程的PCB共享其所在进程的地址空间,  "轻量"就体现在此处
     
  • 一个进程的所有线程具有相同的地址空间, 它们属于同一个线程组, 这样来说进程就可以看作是一个线程组
     
  • 对于进程来说, 同一个地址(虚拟地址), 在不同的进程中, 反复使用而不冲突. 原因是虽然不同进程的虚拟地址一样, 但相同的虚拟地址通过不同的页表映射着不同的物理内存单元, 最终访问到不同的物理页面.
     
  • 线程与进程不同, 两个线程具有各自独立的PCB,但其页表映射相同是相同的,  所以两个PCB共享一个地址空间.
     
  • 实际上, 无论是创建进程的fork, 还是创建线程的库函数pthread_create,底层实现都是调用同一个系统调用接口clone( ).
    如果复制对方的地址空间, 那么就"clone" 出一个 "进程". 如果共享对方的地址空间, 就"clone"出一个"线程" .
     
  • 所以, Linux内核是不区分进程和线程的. 只在用户层面上进行区分. 也是因为如此, 线程所有操作函数 pthread_系列都是库函数,而非系统调用接口

          

线程间数据的独有与共享(同组线程)

  共享

因为同组线程共享进程的地址空间, 所以进程的代码段, 数据段都是共享的, 定义一个函数, 或全局变量, 线程都是可以访问

的, 除此之外, 线程还共享一些进程的资源和环境, 如下:

  • 文件描述符表
     
  • 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
     
  • 当前工作目录
     
  • 用户id(UID), 和组id(GID)

   独有

线程共享进程数据, 但为了防止调用栈混乱, 线程也拥有自己的一部分数据

  • 线程id(线程唯一标识符)
     
  • 栈空间(拥有自己的函数栈)
     
  • 寄存器(保存线程切换前的寄存器集合的状态, 以便下次恢复)
     
  • 信号屏蔽字(由于线程所执行任务的不同, 线程所感兴趣的信号屏蔽字也不同, 需要独有, 但同组线程共享信号处理方法)
     
  • errno(错误码)
     
  • 线程优先级(线程要被CPU调度, 所以和进程一样, 有优先级)

线程的优点--("轻"+多任务并发/并行处理的优势(对比于进程))

先来了解两个概念

并发 : 人们把CPU运行间划分成若干个时间段, 再将不同的时间段分配给各个进程. 这样, 在一个时间段内某个进程的代码

运行时, 其它线程处于挂起状. 当时间片较小, CPU轮转非常快时, 宏观上有多个进程被同时执行的效果. 这种方式我们称之

为并发(Concurrent).

并行 : 在单处理器中多道程序设计系统中, 进程被交替执行, 表现出一种并发的外部特征. 在多处理器系统中, 进程不仅可以

交替执行, 而且可以在同一时刻重叠执行(分别执行在同一台机器不同的CPU上).

 总的来说 "轻" 体现在, 操作系统对于线程和进程的操作来说, 线程的各种操作时空消耗都要更小. 具体来说:

  • 线程间通信更加灵活(可以通过全局变量和函数传参的方式)
     
  • 线程的创建销毁成本更低
     
  • 线程的调度成本更低

  多任务并发/并行处理优势

  • 能充分利用多处理器的可并行数量
     
  • 在等待慢速I/O操作结束的时间,线程可执行其他的计算任务
     
  • 计算(CPU)密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现并行处理, 提高效率
     
  • I/O密集型应用, 为了提高性能, 将I/O操作重叠. 线程可以同时等待不同的I/O操作.

线程的缺点

  • 性能损失

一个很少被外部事件阻塞的CPU密集型线程往往无法与其它线程共享同一个处理器. 如果CPU密集型线程的数量比可用的处理器多,  那么可能会有较大的性能损失, 这里的性能损失指的是增加了额外的同步和调度开销,  而可用的资源(CPU)不变.

  • 健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,任一线程奔溃会导致整个进程崩溃

  • 缺乏访问控制

多个线程对同一个数据同时进行操作时,可能出现线程安全问题,导致数据发生错误

  • 多线程程序调试困难

多线程程序调试困难, gdb不支持调试 .

   注 : 虽然有缺点, 但优点更突出, 所以问题不是很大.

多线程与多进程的比较

线程和进程在处理多任务上一些相同的优点 :

  • 对于CPU密集型程序(多个执行流在有多个CPU资源时可以实现并行提高处理效率)(CPU资源足够)
     
  • 对于I/O密集型程序(多个执行流可以并行压缩等待I/O的时间)(并不一定要CPU资源足够)

总的来说, 在多进程或多线程在处理多任务上, 不能单纯的说谁好谁坏, 各自有各自的优缺点, 具体要看使用场景.

结合上面说的线程的优缺点, 再对比进程具有独立性, 更加的稳定, 健壮的特点, 有如下结论:

  • 程序对主功能的安全性, 稳定性要求更高的场景, 最好使用多进程, 比如说服务器端程序, 我们常用的Shell程序.
     
  • 剩下的考虑多线程(因为线程对比于进程的优点, 剩余场景大多考虑多线程)

比如服务器端, 服务器面向非常多的普通用户, 如果在处理其中一个用户的任务时崩了, 如果是多线程处理, 就会导致整个

程序奔溃, 但如果是多进程, 崩掉一个, 并不会影响其他用户的任务处理, 更加稳定健壮

在很多时候都采用多线程编程,但还是要视具体任务和具体机器的配置情况而定, 比如说:

对于单核CPU的机器, 如果是CPU密集型任务, 如解压文件, 多线程的性能反而不如单线程性能, 因为解压文件需要一直占用

CPU资源, 如果采用多线程, 多线程的频繁切换导致的开销反而会让性能下降. 而对于多核CPU, 对于解压文件来说, 多线程

肯定优于单线程, 多个线程能够并发的更加充分利用每个核的资源. 但还是要注意, 在CPU资源足够的情况下, 可以使程序得

性能更高, 但并非线程越多越好, 调度也需要时间成本.

虽然多线程能够提升程序性能, 但是相对于单线程来说, 它的编程要复杂地多,  要考虑线程安全问题. 因此, 在实际编程过程中, 要

根据实际情况具体选择.

Linux 多线程(线程控制(创建/终止/等待/分离))

写在另一篇博客, 戳链接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/104613224

发布了223 篇原创文章 · 获赞 639 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_41071068/article/details/104571277