【Linux系统化学习】线程概念

2024-03-03 23:52

本文主要是介绍【Linux系统化学习】线程概念,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

线程的概念

线程的引出

什么是线程

理解线程比进程更加的轻量化

线程的优点

现成的缺点

线程异常

线程用途

Linux进程VS线程

线程的简单现象


线程的概念

有关操作系统的书籍或者课本都会这样描述线程:

  • 线程是比进程轻量化的一种执行流
  • 线程是进程内部的一种执行流

线程的引出

线程的概念让我们先从之前的进程谈起

一个进程的产生,首先要将代码和数据从磁盘加载到内存中;然后创建虚拟地址空间和页表,让我们的虚拟空间和物理空间联系起来;最后操作系统给这个进程创建PCB,将PCB和虚拟地址空间建立联系,这样PCB通过虚拟地址空间和页表可以找到物理内存中可执行程序的代码。这样一系列创建进程的流程对于进程来说:地址空间是进程的资源窗口。

一个进程的出现从零到一,从无到有;需要创建PCB,申请资源空间,各种初始化,加载动态库,加载代码,各种映射;整个过程耗时耗力,成本非常高;有没有一种可能我们创建一种特殊的”进程“,只复用几经含有的进程的PCB进行创建PCB,指向这个已经含有进程的虚拟地址空间。通过各种方法把不共享的区域拆成几分,共享的保留。每个”进程“该执行自己的执行自己的,该共享的共享。将页表进行切割,实现区分。

和创建进程相比较,这个特殊的进程只需要创建一个PCB,创建非常的简单,没有各种初始化和加载,只参与进程资源的分配。非常的轻量化。在Linux操作系统中这个特殊的轻量化进程就是我们的线程。

什么是线程

  • 在一个程序里的一个执行路线就叫做线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”
  • 一切进程至少都有一个执行线程
  • 线程在进程内部运行,本质是在进程地址空间内运行
  • 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化
  • 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流

因此线程的本质:

  • 线程是CPU调度的基本单位
  • 线程是承担系统资源的基本实体。

那么对于之前文章中的进程,是只含有一个执行流的进程,今天的进程含有多个执行流,每个执行流就是一个进程,也是一个线程。

理解线程比进程更加的轻量化

CPU对于一个进程的切换和一个进程的产生一样非常麻烦;需要替换页表,虚拟地址空间,代码等各种数据。而对于一个线程来说只需要替换PCB既可,线程通过对页表的切割;每个线程依然可以通过页表找到或者访问到自己的数据。其实这样的切换对于现在CPU的速度来说是可以忽略不计的。

首先我们要清楚局部性原理,对于CPU来说目前我们正在处理第10行代码有很大的概率接下来会处理第11行及以下的代码。根据冯诺依曼体系结构和木桶原理,CPU再对第11行代码进行处理的时候势必要从内存中重新加载代码。因此我们可以在CPU中添加一个缓存(cache),根据局部性原理,将接下来有很大概率被处理的代码预加载到这个缓存中即可;保存在缓存中的数据称为热数据。那么我们在回头来看切换一个进程,还需要将这个热数据进行切换,而线程则不需要。因此线程是比进程更加的轻量化。

线程的优点

  • 创建一个新线程的代价要比创建一个新进程小得多
  • 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
  • 线程占用的资源要比进程少很多
  • 能充分利用多处理器的可并行数量
  • 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
  • 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
  • I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。

现成的缺点

性能损失

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

健壮性降低

编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。

缺乏访问控制

进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。

编程难度提高

编写与调试一个多线程程序比单线程程序困难得多

线程异常

  • 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃
  • 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出

线程用途

  • 合理的使用多线程,能提高CPU密集型程序的执行效率
  • 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)

Linux进程VS线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位

线程共享进程数据,但也拥有自己的一部分数据:

线程ID
一组寄存器
栈(动态运行)
errno
信号屏蔽字
调度优先级                                                                                                                                          硬件上下文(动态切换)

程的多个线程共享 同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

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

进程和线程的关系如下图:

线程的简单现象

  7 8 // 新线程9 void *ThreadRoutine(void *arg)10 {11     const char *threadname = (const char *)arg;12     while (true)              13     {14         std::cout << "I am a new thread: " << threadname << ", pid: " << getpid() << std::endl;15         sleep(1);                                                                                                                     16     }17 }18                  19 int main()20 {21     // 已经有进程了22     pthread_t tid;23     pthread_create(&tid, nullptr, ThreadRoutine, (void *)"thread 1");24                    25                   26                                                                      27     // 主线程28     while (true)29     {30         std::cout << "I am main thread"31                   << ", pid: " << getpid()  << std::endl;32         sleep(1);33     }                                  34     return 0;}

上面的代码我们在主线程中创建了一个线程,两个线程含有相同的PID;而主线程的PID和LWP是一样的;LWP(Light Weight Process)译为轻量级进程的意思。


今天对Linux下线程的概念的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!

这篇关于【Linux系统化学习】线程概念的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/771339

相关文章

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

linux下多个硬盘划分到同一挂载点问题

《linux下多个硬盘划分到同一挂载点问题》在Linux系统中,将多个硬盘划分到同一挂载点需要通过逻辑卷管理(LVM)来实现,首先,需要将物理存储设备(如硬盘分区)创建为物理卷,然后,将这些物理卷组成... 目录linux下多个硬盘划分到同一挂载点需要明确的几个概念硬盘插上默认的是非lvm总结Linux下多

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

Java多线程父线程向子线程传值问题及解决

《Java多线程父线程向子线程传值问题及解决》文章总结了5种解决父子之间数据传递困扰的解决方案,包括ThreadLocal+TaskDecorator、UserUtils、CustomTaskDeco... 目录1 背景2 ThreadLocal+TaskDecorator3 RequestContextH

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程