【Linux线程】线程的深度解析(线程是什么?线程与进程区别是什么?)

news2024/9/22 3:55:03

目录

一、前言

二、 什么是线程

💧线程的引入💧 

💧线程的基本概念 💧

💧线程的理解 💧 

💧进程与线程的关系💧

💧程序如何划分(重拾页表、见一下LWP)💧 

三、简单的使用线程

四、线程小结 

🔥再谈线程🔥 

🔥线程的优点 🔥

🔥线程的缺点🔥 

🔥线程和进程之间的区别🔥 

🔥线程的用途 🔥

五、共勉 


一、前言

        将一份【代码成功编译】后,可以得到一个【可执行程序】,程序运行后,相关代码和数据被 【load】 到内存中,并且操作系统会生成对应数据结构(比如 PCB)对其进行管理及分配资源,准备工作做完之后,我们就可以得到一个运行中的程序,简称为 进程,对于操作系统来说,只有 进程 的概念是无法满足高效运行的需求的,因此需要一种执行粒度更细、调度成本更低的执行流,而这就是 线程

 Windows 中的线程

二、 什么是线程

【线程】是操作系统管理任务执行(CPU)的基本单位,它允许一个程序同时执行多个任务(进程中有很多执行流),从而实现并发(在同一时间段内处理多个任务)或并行(同时处理多个任务)。 

💧线程的引入💧 

想要真正的了解【线程】,就需要先了解【进程】因为线程是从进程中延申出来的,由于它们之间的概念十分的枯燥难以理解,我们用一个流程图,来给大家清楚的说明,线程和进程之间到底有那些千丝万缕的联系。

  • CPU与进程:CPU比作工厂,它一次只能处理一个车间(进程)的任务。如果一个车间正在使用电力,其他车间(进程)就必须等待。

  • 进程进程就好比是工厂中的车间,代表CPU正在执行的任务。在任一时刻,CPU只能运行一个进程,而其他进程则处于非运行状态。

  • 线程:线程比作车间里的工人。一个车间(进程)可以有很多工人(线程),他们协同工作以完成一个任务。一个进程可以包含多个线程。

  • 共享内存空间:车间的空间是工人们共享的,这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

  • 互斥锁(Mutex:为了防止多个线程同时读写某一块内存区域,可以使用互斥锁。这就像是一个房间里面有人的时候,其他人就不能进去,必须在门口排队等待。

  • 资源限制有些房间(内存区域或资源)最多只能容纳一个人,这代表某些资源一次只能被一个线程使用。

 看完这幅图和解析,我相信大家已经能够对 进程和线程有了一个基本的认识,下面我们再来深度的解析,线程

💧线程的基本概念 💧

教材观点】

  1. 【线程】就是【进程】一个执行分支、执行粒度比进程更细、调度成本更低
  2. 线程就是进程内部的一个执行流

【内核观点】 

  • 【进程】承担系统资源分配的基本实体,而 【线程】是 CPU 运行的基本单位

💧线程的理解 💧 

【线程】是对以往【进程】概念的补充完善,正确理解线程概念是一件十分重要的事,理解 线程 之前需要先简单回顾一下 进程】

  • 程序运行后,相关的代码和数据会被 load 到内存中,然后操作系统为其创建对应的 PCB 数据结构、生成虚拟地址空间、分配对应的资源,并通过页表建立映射关系

详见:《Linux【进程地址空间】》 

 进程之间是相互独立

  • 即使是 父子进程,他们也有各自的 虚拟地址空间、映射关系、代码和数据(可能共享部分数据,出现修改行为时引发 写时拷贝机制) 
  • 如果我们想要创建 其他进程 执行任务,那么 虚拟地址空间、映射关系、代码和数据 这几样东西是必不可少的,想象一下:如果只有进程的概念,并且同时存在几百个进程,那么操作系统调度就会变得十分臃肿 
  • 操作系统在调度进程时,需要频繁保存上下文数据、创建的虚拟地址空间及建立映射关系

线程的出现

  • 为了避免这种繁琐的操作,引入了 【线程】 的概念,所谓 【线程】 就是:额外创建一个 task_struct 结构,该 task_struct 同样指向当前的虚拟地址空间,并且不需要建立映射关系及加载代码和数据,如此一来,操作系统只需要 针对一个 task_struct 结构即可完成调度,成本非常低

 为什么【切换进程】比 【切换线程开销大得多?

  • 在 CPU 内部包括:运算器、控制器、寄存器、MMU、硬件级缓存(cache,其中 硬件级缓存 cache 又称为 高速缓存,遵循计算机设计的基本原则:局部性原理,会预先加载 部分用户可能访问的数据 以提高效率。
  • 如果【切换进程】,会导致 高速缓存 中的数据无法使用(进程具有独立性),重新开始 预加载,这是非常浪费时间的(对于 CPU 来说)。
  • 但【切换线程】就不一样了,因此线程从属于进程,切换线程时,所需要的数据的不会发生改变,这就意味值 高数缓存 中的数据可以继续使用,并且可以接着 预加载 下一波数据

进程(process的 task_struct 称为 PCB线程(thread的 task_struct 则称为 TCB

  • 从今天开始,无论是 【进程 还是 【线程,都可以称为 执行流线程 从属于 进程当进程中只有一个线程时,我们可以粗粒度的称当前进程为一个单独的执行流;当进程中有多个线程时,则称当前进程为多执行流,其中每一个执行流就是一个个的线程 

总结:执行流的调度由操作系统负责,CPU 只负责根据 task_struct 结构进行计算 

  • 若下一个待调度的执行流为一个单独的进程,操作系统仍需创建 PCB 及 虚拟地址空间、建立映射关系、加载代码和数据
  • 但如果下一个待调度的执行流为一个线程,操作系统只需要创建一个 TCB,并将其指向已有的虚拟地址空间即可

现在面临着一个很关键的问题:进程和线程究竟是什么关系? 


💧进程与线程的关系💧

进程】是 承担系统资源分配的实体,比如 程序运行必备的:虚拟地址空间、页表映射关系、相关数据和代码 这些都是存储在 进程 中的,也就是我们历史学习中 进程 的基本概念 

线程】是 CPU 运行的基本单位,程序运行时,CPU 只认 task_struct 结构,并不关心你是 【线程】 还是 【进程】,不过,线程 包含于 进程 中,一个 进程 可以只有一个 线程,也可以有很多 线程,当只有一个 线程 时,通常将其称为 进程,但对于 CPU 来说,这个 进程 本质上仍然是 线程;因为 CPU 只认 task_struct 结构,并且 PCB 与 TCB 都属于 task_strcut,所以才说 线程是 CPU 运行的基本单位

总结:

  • 进程】是由操作系统将程序运行所需地址空间、映射关系、代码和数据打包后的资源包
  • 线程/轻量级进程/执行流 】则是利用资源完成任务的基本单位

线程包含于进程中,进程本身也是一个线程

我们之前学习的进程概念是不完整的,引入线程之后,可以对进程有一个更加全面的认识

  • 通常将程序启动,比如 main 函数中的这个线程称为 主线程,其他线程则称为 次线程 

实际上 进程 = PCB + TCB + 虚拟地址空间 + 映射关系 + 代码和数据,这才是一个完整的概念

以后谈及进程时,就要想到 一批执行流+可支配的资源

【进程】与【线程】的概念并不冲突,而是相互成就 

  • Linux 中,认为 【PCB】 与 【TCB】 的共同点太多了,于是直接复用了 PCB 的设计思想和调度策略,在进行 【线程管理】 时,完全可以复用 【进程管理】 的解决方案(代码和结构),这可以大大减少系统调度时的开销,做到 小而美,因此 Linux 中实际是没有真正的 线程 概念的,有的只是复用 PCB 设计思想的 TCB
  •  在这种设计思想下,【线程】 注定不会过于庞大,因此 Linux 中的 线程 又可以称为 轻量级进程(LWP轻量级进程 足够简单,且 易于维护、效率更高、安全性更强,可以使得 Linux 系统不间断的运行程序,不会轻易 崩溃

在Linux系统中,所有的执行流都被称为轻量级进程(Lightweight Process,LWP),实际上就是操作系统概念中的线程。在Linux中,线程和进程的区别并不是很明显,因为Linux将线程实现为与进程相似的实体,即轻量级进程。 

  • 在Linux中,每个【轻量级进程】(线程)都对应一个task_struct结构体,操作系统通过调度算法选择下一个要执行的轻量级进程,而不关心这个task_struct属于哪个进程,或者是属于一个进程的其中一个线程。
  • 因此,在Linux中,CPU调度的实际执行单元是轻量级进程(线程),而不是进程。每个轻量级进程都有自己的执行流,可以独立执行代码,拥有独立的栈空间和寄存器状态。多个轻量级进程可以共享一个进程的资源,实现并发执行。

💧程序如何划分(重拾页表、见一下LWP)💧 

操作系统进行内存管理的基本单位是【4KB

内存里都是以【4kb】大小分的一个一个内存块——空间

可执行程序也是以【4kb】进行分——内容

现在我们再来重新看待页表: 

  • 后12位叫做叶内偏移
  • 同时读取时还要结合数据类型的大小

总结:多个执行流即不同的线程执行不同的代码,获得各自的数据,本质就是让不同的线程各自看到不同的页表。

三、简单的使用线程

如何验证 Linux 中的【线程】呢? 简单使用一下就好了

接下来简单使用一下 pthread 线程原生库中的线程相关函数(只是简单使用,不涉及其他操作)

  • 给不同的线程分配不同的区域,本质就是给让不同的线程,各自看到全部页表的子集 
#include <iostream>
#include <thread>
#include <unistd.h>
#include <sys/types.h>

using namespace std;

// 线程分支
void *test(void *arg)
{
    while (true)
    {
        // 在线程中打印进程PID
        std::cout << "I am new thread, pid: " << getpid() << std::endl;
        sleep(1);
    }
}

int main()
{
    // 线程id
    pthread_t tid;
    pthread_create(&tid, nullptr, test, nullptr);  //创建一个新线程
    while (true)
    {
        // 打印主线程的 进程PID
        std::cout << "I am main thread, pid: " << getpid() << std::endl; 
        sleep(1);
    }
    return 0;
}
  • 函数编译完成,会出现两条分支,一条是主线程,一条之分支线程,我们需要观察吗,这两个线程是否在同一个进程内,这两个线程会有区别吗?

  • 编译程序时,需要带上 -lpthread 指明使用 线程原生库
  • 结果:主线程+一个次线程同时在运行

 使用指令查看当前系统中正在运行的 线程 信息

while :; do ps -aL | head -1 && ps -aL | grep thread ; echo "-----------------------------------"; sleep 1 ; done

可以看到此时有 两个个线程 

  • 细节1:两个个线程的 PID 都是 4071708
  • 细节2:个线程的 LWP 各不相同
  • 细节3:第一个线程的 PID 和 LWP 是一样的

其中,第一个线程就是 主线程,也就是我们之前一直很熟悉的 进程,因为它的 PID 和 LWP 是一样的,所以只需要关心 PID 也行 

操作系统如何判断调度时,是切换为 线程 还是切换为 进程 ? 

  • 将待切换的执行流 PID 与当前执行流的 PID 进行比对,如果相同,说明接下来要切换的是 线程,否则切换的就是 进程
  • 操作系统只需要找到 LWP 与 PID 相同的线程,即可轻松锁定主线程

线程是进程的一部分,给其中任何一个线程发送信号,都会影响到其他线程,进而影响到整个进程 


四、线程小结 

🔥再谈线程🔥 

Linux 中没有 真线程,有的只是复刻 进程 代码和管理逻辑的 轻量级线程(LWP 

线程 有以下概念: 

  • 在一个程序中的一个执行路线就叫做 线程(Thread),或者说 线程 是一个进程内部的控制程序
  • 每一个进程都至少包含一个 主线程
  • 线程 在进程内部执行,本质上仍然是在进程地址空间内运行
  • Linux 系统中,CPU 看到的 线程 TCB 比传统的 进程 PCB 更加轻量化
  • 透过进程地址空间,可以看到进程的大部分资源,将资源合理分配给每个执行流,就形成了 线程执行流

🔥线程的优点 🔥

 线程 最大的优点就是 轻巧、灵活,更容易进行调度

  • 创建一个线程的代价比创建一个进程的代价要小得多
  • 调度线程比调度进程要容易得多
  • 线程占用的系统资源远小于进程
  • 可以充分利用多处理器的并行数量(进程也可以)
  • 在等待慢速 IO 操作时,程序可以执行其他任务(比如看剧软件中的 “边下边看” 功能)
  • 对于计算密集型应用,可以将计算分解到多个线程中实现(比如 压缩/解压 时涉及大量计算)
  • 对于 IO密集型应用,为了提高性能,将 IO操作重叠,线程可以同时等待资源,进行 高效IO(比如 文件/网络 的大量 IO 需要,可以通过 多路转接 技术,提高效率)

线程 的合理使用可以提高效率,但 线程 不是越多越好,而是 合适 最好,让每一个线程都能参与到计算中 

🔥线程的缺点🔥 

线程 也是有缺点的: 

  • 性能损失,当 线程 数量过多时,频繁的 线程 调度所造成的消耗会导致 计算密集型应用 无法专心计算,从而造成性能损失 
  • 健壮性降低,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的
  • 缺乏访问控制,进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响 

 🔥线程和进程之间的区别🔥 

 虽然进程和线程之间很相似,但是它们之间还是存在着一些细小的区别

  • 进程:是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的实例。每个进程都有自己的独立内存空间。
  • 线程:是进程中的执行单元,多个线程共享同一进程的内存空间和资源,但每个线程有自己的栈、程序计数器等。线程是CPU调度的基本单位

内存空间:

  • 进程:每个进程都有自己独立的内存空间,包括代码段、数据段、堆和栈。进程之间的内存是隔离的,不能直接访问对方的内存空间。
  • 线程:线程共享进程的内存空间,因此可以直接访问同一进程中的其他线程的数据,但这也带来了同步和并发控制的复杂性。

资源开销 

  • 进程:创建和切换进程的开销较大,因为涉及到完整的内存空间、资源的分配与回收。
  • 线程:线程创建和切换的开销相对较小,因为线程共享进程的资源和内存,切换时不需要进行完整的资源分配和回收。

通信方式 

  • 进程:进程间通信(IPC)需要通过操作系统提供的机制,如管道、消息队列、共享内存、信号量等,通信相对复杂且效率较低。
  • 线程:线程之间可以通过共享内存直接通信,通信效率较高,但需要小心处理同步问题,以避免竞态条件和死锁。

 并发性

  • 进程:由于进程是独立的单元,一个进程的崩溃通常不会影响其他进程。但进程间的并发性较低。
  • 线程:线程之间的并发性较高,可以在同一进程内并行执行任务。然而,一个线程的崩溃可能导致整个进程的崩溃。

进程与线程的一个简陋模型用图表示的话,是这样的: 


🔥线程的用途 🔥

 合理的使用 多线程,可以提高 CPU 计算密集型程序的效率

  • 进程:适合用于多任务、多用户的独立应用,如操作系统中的服务程序、独立的应用程序等。
  • 线程:适合用于需要并行执行共享大量数据的任务,如服务器的多线程处理、实时图像处理等。

五、共勉 

 以下就是我对【Linux系统编程】线程的深度解析 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新【Linux系统编程】请持续关注我哦!!!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051205.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行业分析---AI时代是不断更新自身技术还是会利用新技术?

1 背景 最近有两个热点新闻&#xff0c;&#xff08;1&#xff09;“孟晚舟建议不要选和机器竞争的职业&#xff0c;根本不是它的对手”&#xff1b;&#xff08;2&#xff09;“周鸿祎建议萝卜快跑把无人出租车卖给司机&#xff0c;可实现三赢”。 技术的变革在互联网领域是比…

QT翻金币小游戏(含音频图片文件资源)

目录 QT翻金币小游戏 音频图片资源文件获取 效果展示 图片 视频 实现代码 main.cpp mymainwindow.h mymainwindow.cpp startscene.h startscene.cpp selectscene.cpp playscene.h playscene.cpp mypushbutton.h mypushbutton.cpp dataconfig.h dataconfig.cpp QT…

音频剪辑用什么工具?试试这三款

音乐&#xff0c;是情感的传递者&#xff0c;是灵魂的慰藉。作为一名音乐人&#xff0c;我一直在探索如何更好地捕捉和表达音乐的精髓。在这个数字化的时代&#xff0c;音频剪辑软件成为了我们表达创意的重要工具。今天&#xff0c;我想从一个音乐人的角度&#xff0c;分享我使…

C语言每日好题(3)

有任何不懂的问题可以评论区留言&#xff0c;能力范围内都会一一回答 #define _CRT_SECURE_NO_WARNING #include <stdio.h> #include <string.h> int main(void) {if ((strlen("abc") - strlen("abcdef")) > 0)printf(">\n")…

CentOS 7 下载/安装

下载 centos安装包下载_开源镜像站-阿里云centos安装包是阿里云官方提供的开源镜像免费下载服务&#xff0c;每天下载量过亿&#xff0c;阿里巴巴开源镜像站为包含centos安装包的几百个操作系统镜像和依赖包镜像进行免费CDN加速&#xff0c;更新频率高、稳定安全。https://mir…

SpringBoot(一)

1.Spring Boot概要 1.1 SpringBoot介绍 随着动态语言的流行&#xff08;Ruby、Scala、Node.js&#xff09;, Java的开发显得格外的笨重&#xff1b;繁多的配置、低下的开发效率、复杂的部署流程以及第三方技术整合难度大。 在上述环境下&#xff0c;Spring Boot由此诞生&#…

每天五分钟计算机视觉:搭建人脸识别的Siamese深度神经网络模型

本文重点 前面的一篇文章中介绍了关于一次学习的问题,解决一次学习问题的关键在于学习到一个函数d,这个d可以计算出两张图片中的人脸是不是同一个人。那么我们需要搭建什么样的神经网络才可以让模型学习出这样的函数d呢?本文我们介绍一下Siamese神经网络结构,它可以帮助我…

快速上手体验MyPerf4J监控springboot应用(docker版快速开始-本地版)

使用MyPerf4J监控springboot应用 快速启动influxdb时序数据库日志收集器telegrafgrafana可视化界面安装最终效果 项目地址 项目简介: 一个针对高并发、低延迟应用设计的高性能 Java 性能监控和统计工具。 价值 快速定位性能瓶颈快速定位故障原因 快速启动 监控本地应用 idea配…

BeagleBone Black 上手

芯片特性 板级功能 资源内存 SDRAM 512MB DDR3L 800MHZ A single 256Mb x16 DDR3L 4Gb (512MB) memory device is used. The memory used is one of two devices: MT41K256M16HA-125 from Micron D2516EC4BXGGB from Kingston It will operate at a clock frequency of 400M…

DDD领域驱动设计的原理与实践

目录 什么是DDD领域驱动设计&#xff1f; 定义与概念&#xff1a; 核心思想&#xff1a; 核心概念&#xff1a; 核心原则&#xff1a; 优势与应用&#xff1a; 与微服务架构和传统三层架构的关系&#xff1a; 理解领域模型 举例 统一语言&#xff08;Ubiquitous Langu…

【C++11】入门基础

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;C从入门至进阶 这里将会不定期更新有关C/C的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目录…

基于Kotlin Multiplatform实现静态文件服务器(三)

Expect 和 Actual expect 关键字用于定义一个多平台通用的声明&#xff0c;即该声明在所有平台上都可用&#xff0c;并且需要在特定平台上实现。actual 关键字通常与 expect 关键字配合使用&#xff0c;用于定义多平台通用的接口和函数&#xff0c;从而允许在不同的平台上使用…

PyTorch--深度学习

onux部署功能 cpu运行时间 3. 自动求导 求导结果为&#xff1a;2 1 1

在java中前后端进行交互使用的内容

前言 本文将讲解在java前后端进行交互时会使用的内容, 过滤器 , 前后端交互时: 同步请求(了解)与异步请求, 后端响应json格式数据, 后端标准响应数据格式 过滤器 首先需要了解什么是过滤器: 过滤器是javaEE中在前向后端发送请求时进行拦截的技术,作用…

Linux系统编程(10)线程资源回收和互斥锁

一、pthread_cancel函数 pthread_cancel 函数用于请求取消一个线程。当调用 pthread_cancel 时&#xff0c;它会向指定的线程发送一个取消请求。 #include <pthread.h>int pthread_cancel(pthread_t thread);thread&#xff1a;要发送取消请求的线程标识符。 成功时&a…

函数递归,匿名、内置行数,模块和包,开发规范

一、递归与二分法 一&#xff09;递归 1、递归调用的定义 递归调用&#xff1a;在调用一个函数的过程中&#xff0c;直接或间接地调用了函数本身 2、递归分为两类&#xff1a;直接与间接 #直接 def func():print(from func)func()func() # 间接 def foo():print(from foo)bar…

mac安装ipd包【金铲铲为例】

mac安装ipd包 安装PlayCover 链接&#xff1a;https://github.com/PlayCover/PlayCover 1、点最新Releases 2、cmd ↓&#xff0c;拉到最下面下载dmg 3、安装 图标拖拽到Applications里 IPA下载 以金铲铲为例&#xff0c;良心砸壳包站点&#xff0c;有能力可以支持一下…

系列:水果甜度个人手持设备检测-行业法律法规

系列:水果甜度个人手持设备检测 --行业法律法规 背景 由于我们目标是制作针对水果的便携或手持式检测仪器&#xff0c;既然是民用产品&#xff0c;必然受一系列法律法规的约束&#xff0c;产品在上市之前将会受各种国家标准和地方标准的检验。本篇章中我们采用启发性搜索的方…

Python自动化:解锁高效工作与生产力的密钥

在当今快节奏的数字时代&#xff0c;自动化已成为提升工作效率、优化流程、减少人为错误的不可或缺的工具。Python&#xff0c;作为一种功能强大、易于学习且应用广泛的编程语言&#xff0c;在自动化领域扮演着举足轻重的角色。无论是数据处理、Web自动化、软件测试&#xff0c…

ETL数据集成丨将SQL Server数据同步至Oracle的具体实现

一、背景 在构建企业级数据架构时&#xff0c;将SQL Server数据库的数据同步至数仓数据库&#xff08;如Oracle&#xff09;是一项至关重要的任务。这一过程不仅促进了跨系统数据的一致性与可用性&#xff0c;还为数据分析、商业智能以及决策支持系统提供了坚实的数据基础。 …