【Linux】初识线程

一、线程的概念

线程是操作系统能够进行运算调度的基本单位,它被包含在进程之中,是进程中的实际运作单位。

定义与基本特征

轻量级实体:线程是比进程更小的可独立运行的基本单位,也被称为轻量级进程。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等,但每个线程都有自己独立的程序计数器、寄存器和栈等。并发执行:同一进程内的多个线程可以并发执行,操作系统会为每个线程分配CPU时间片,使得它们在宏观上看起来是同时运行的。通过这种方式,线程可以实现程序的并发处理,提高系统资源的利用率和程序的执行效率。共享与独立:线程可以共享进程的大部分资源,这使得线程之间的通信和数据共享相对容易实现。同时,每个线程又有自己独立的执行路径和上下文,能够独立地进行运算调度,互不干扰。

线程的状态

就绪状态:线程已经具备了运行的条件,等待操作系统分配CPU资源。一旦获得CPU时间片,就可以立即执行。运行状态:线程正在CPU上执行,正在执行相应的任务代码。阻塞状态:线程因等待某些事件的发生而暂时无法继续执行,比如等待I/O操作完成、等待获取锁、等待其他线程的通知等。当所等待的事件发生后,线程会从阻塞状态转换为就绪状态,重新进入就绪队列等待CPU调度。

作用与优势

提高程序响应性:在图形界面应用程序中,通常会有一个主线程用于处理界面的显示和更新,同时还可以创建其他线程来处理后台任务,如文件读取、网络数据下载等。这样,即使后台任务比较耗时,也不会阻塞主线程,从而保证界面能够及时响应用户的操作,提高用户体验。充分利用多核处理器:在多核处理器的环境下,多个线程可以同时在不同的核心上运行,实现真正的并行处理,从而充分发挥多核处理器的性能优势,提高程序的执行效率。简化程序结构:将一个复杂的任务分解为多个线程来执行,可以使程序的结构更加清晰,每个线程负责一个相对独立的子任务,便于代码的编写、调试和维护。

与进程的关系

立即进入“豆包AI人工智官网入口”;

立即学习“豆包AI人工智能在线问答入口”;

包含关系:进程是资源分配的基本单位,而线程是进程内的执行单元。一个进程可以包含一个或多个线程,线程是进程的组成部分,不能独立于进程而存在。资源共享与独立:进程拥有独立的地址空间和系统资源,不同进程之间的资源相互隔离,而同一进程内的线程共享进程的资源,但每个线程有自己独立的栈空间和寄存器等。调度与切换:进程的调度和切换相对复杂,需要保存和恢复大量的上下文信息,开销较大,而线程的调度和切换相对简单,因为线程共享进程的资源,所以在切换时只需保存和恢复少量的寄存器等信息,开销较小。

线程的缺点

性能损失:一个很少被外部事件阻塞的计算密集型线程往往无法与其他线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,因为这里增加了额外的同步和调度的开销,但可用的资源是不变的。健壮性降低:在一个多线程的程序中,多线程共享的资源是占大部分的。如果线程之间时间分配上出现了细微的偏差或者共享了不该共享的变量,就很有可能会造成不良影响,所以进程间缺乏保护。线程异常:线程一旦出现除零或野指针这样的错误,导致线程崩溃,进程也会跟着崩溃。

在操作系统中,线程是实现并发编程和提高系统性能的重要手段,广泛应用于各种类型的程序开发中。

这里我们举一个简单的例子帮助大家理解。我们都知道,盖一栋大楼需要土木的同学画图纸,建筑工人打地基,吊车工人运送原料,设计的同学设计室内,货车司机运水泥等等。他们共同在做的就是盖大楼,但他们每个人都在做自己的事情,这些事情是无法分开的,但合起来就是盖楼的一个过程,这就是我们进程和线程之间的关系。每个人做的工作都是线程,所有人做的工作合起来就是进程。

我们以前说的进程就是单线程进程,开个玩笑说一个人把楼给盖起来的。

所以我们要理解的是,进程和线程真正的概念。进程是承担分配系统资源的基本实体,线程是我们进程内部的执行流资源。简单来说,由进程来帮我们申请空间分配资源,线程在进程分配的资源中进行线程的执行。

线程和进程的切换问题。线程作为一个轻量级的进程,在CPU中也是要进行切换的。一个进程中的线程共享这个进程的时间片,描述线程的数据结构也是task_struct结构,当然其中有标识它是线程的标识符id。在我们CPU执行进程的时候,会将缓存数据加载到寄存器cache中,在线程进行切换的时候,由于共享数据的特性,cache中的数据不用进行更换,效率高,时间片结束后,进程切换时,才进行cache数据更换。

二、再谈进程地址空间

今天我们借线程这个话题再谈进程地址空间。线程是进程的细分,这个细分也要由页表找到物理内存,它是怎么找到的呢,这里我们就不得不讲解一下页表的实现结构了。

实际上,页表并不像我们前面说的一列是虚拟内存地址,另一列是物理内存地址,它们一一对应,这只是我们抽象出来的,忽略了页表自身结构,而保留它的功能的一个抽象的概念。有一个很简单的问题,如果页表的实现是这样的话,以32位计算机为例,它的一个地址就需要页表中4个字节来存储,而我们的虚拟地址中有页的概念,一页4KB,虚拟内存一共有多少页那页表就有多少虚拟地址,已知虚拟内存共4GB,也就是4*1024*1024÷4个地址,那光虚拟内存就要4*1024*1024bytes = 4MB连续的物理内存,并且大部分进程实际使用的虚拟地址空间只是其中一小部分,这就意味着页表中大量的页表项是无效的,却依然占用着物理内存,造成了极大的浪费,所以我们就要引出真正的页表结构了。

多级页表

我们页表的结构就是多级页表,上面所说的这种页表结构叫做一级页表,这样的页表我们也看到了,会占用大量连续的物理内存,并且很多情况下大部分页表项可能都是无效的,造成内存浪费,是不可行的。多级页表结构通过将页表分级,仅在需要时才分配和使用各级页表,从而有效减少内存占用

二级页表

虚拟地址需要32个比特位来存储,我们将这32个比特位分开存储,前十个,中间十个,最后十二个共三个部分。第一部分叫做外层页表,第二部分叫做内层页表,第三部分叫做页内偏移。

外层页表(页目录)

前十个比特位叫做外层页表(页目录),页目录中的每个条目都指向相对应的一个内层页表。把前十位看做一个十进制数字,这个数字的大小就是页目录的下标,页目录占用空间的大小为2^10*4 bytes = 4KB,页目录的条目数和内层页表的条目数相同,是一一对应的。

内层页表(二级页表)

中间十个比特位叫做内层页表(二级页表),二级页表中的每个条目都指向物理地址空间的一个页。把中间十位看做一个十进制数字,这个数字的大小,就是该地址在物理地址空间的页码下标。一个二级页表的大小也是4KB,但是二级页表一般都是不全的,申请才有,不申请就没有。

页内偏移

最后十二个比特位叫做页内偏移(偏移量)。前面已经指向了物理地址空间的一个页了,我们知道页的大小就是4KB,也就是2^12字节,而最后剩下十二个比特位正是用来形容它的。我们知道每个字节都有一个地址,这里页内偏移就是地址相对于这个页起始地址的偏移量。

【Linux】初识线程

三、线程创建

函数pthread_create

用于创建一个新线程。

#include int pthread_create(pthread_t *thread, const pthread_attr_t *attr,                   void *(*start_routine) (void *), void *arg);

返回值:成功返回0,失败返回非零错误码。

thread:这是一个指向 pthread_t 类型变量的指针,pthread_t 是一个线程标识符类型,用于唯一标识一个线程。当 pthread_create 成功创建一个新线程时,会将该线程的标识符存储在 *thread 所指向的内存位置。

attr:这是一个指向 pthread_attr_t 类型的常量指针,用于指定新线程的属性。如果将其设置为 NULL,则表示使用默认的线程属性。pthread_attr_t 类型定义了一系列线程的属性,如线程的栈大小、调度策略、分离状态等。可以使用 pthread_attr_init 函数初始化一个 pthread_attr_t 对象,并使用其他相关函数来设置具体的属性,但是我们一般用不到这些属性,我们线程用来就是实现多任务调度的。

start_routine:这是一个函数指针,指向新线程开始执行时要调用的函数。该函数必须接受一个 void * 类型的参数,并返回一个 void * 类型的值。新线程从这个函数开始执行,直到该函数返回或者线程被取消。

arg:这是传递给 start_routine 函数的参数。由于 start_routine 函数的参数类型是 void*,因此可以将任意类型的数据指针转换为 void* 类型传递给该函数。在 start_routine 函数内部,需要将其转换回原来的类型,这个我们下一篇文章再谈,这里就简单使用一下。

测试

下面是一个简单的测试线程创建的代码。主线程和新线程的任务都是循环打印自己的pid。

#include #include #include void *threadRun(void* args){    while(1)    {        std::cout 

【Linux】初识线程

注意这里的makefile文件,我们将所对应的库写上了,这个库叫做POSIX线程库,它并不是操作系统原生自带,但在Linux系统中都会有这个库,因为Linux内核提供的clone()函数调用更加复杂,clone是Linux内核用来创建轻量级进程的函数,我们的pthread_create()就是基于它封装的,使用方便。

简单看一下clone函数,确实是要比pthread_create()要难用的。

#include int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

【Linux】初识线程

我们查看打印结果,虽然因为进程调度的原因偶尔会出现重叠乱打的情况,但是大部分时间还是正常的。从这个乱打的情况下我们可以发现,线程之间是不存在同步和互斥的概念的。我们还可以通过打印结果发现一个特点,它们的进程pid是相同的,即一个进程中的多个线程共享一个进程pid。

在进程执行的过程中,我们可以通过命令ps -aL查看线程资源。我们线程也有自己的唯一标识符LWP表示light weight process也就是线程。我们发现两个线程的PID相同这我们上面说了,其中一个线程LWPPID相同,这个线程就是主线程。

【Linux】初识线程

今日分享就到这了~

以上就是【Linux】初识线程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/207324.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
vscode如何自定义状态栏 vscode底部栏的调整方法
上一篇 2025年11月3日 01:24:11
小红书店铺怎么同步淘宝商品?小红书店铺商品合集怎么弄
下一篇 2025年11月3日 01:24:15

相关推荐

  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • 如何让动态追加元素的类事件生效?

    如何在追加元素后使其绑定类事件生效 在页面中引入三方 JavaScript 类并通过添加相应 class 来调用事件方法是一种常见的做法。然而,如果通过 JavaScript 追加标签元素,即使添加了对应的 class,事件也可能无法生效。 为了解决这个问题,可以尝试以下步骤: 检查追加的标签是否为…

    2026年5月10日
    000
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    000
  • 修复点击时按钮抖动:CSS垂直对齐实践

    本文探讨了在Web开发中,交互式按钮(如播放/暂停按钮)在点击时发生意外垂直位移的问题。通过分析CSS样式变化对元素布局的影响,我们发现这是由于按钮不同状态下的边框样式和内边距改变,以及默认的垂直对齐行为共同作用所致。核心解决方案是利用CSS的vertical-align属性,将其设置为middle…

    2026年5月10日
    100
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • JavaScript 闭包:理解闭包原理与内存泄漏问题

    闭包是函数访问其外部作用域变量的能力,即使外部函数已执行完毕。如 inner 函数引用 outer 中的 count,形成闭包,使变量持久存在。闭包本身无害,但可能因延长变量生命周期导致内存泄漏,例如事件监听器引用大对象时。若未及时清理 DOM 事件或定时器,闭包会阻止垃圾回收,造成内存占用过高。解…

    2026年5月10日
    100
  • c++如何实现UDP通信_c++基于UDP的网络通信示例

    UDP通信基于套接字实现,适用于实时性要求高的场景。1. 流程包括创建套接字、绑定地址(接收方)、发送(sendto)与接收(recvfrom)数据、关闭套接字;2. 服务端监听指定端口,接收客户端消息并回传;3. 客户端发送消息至服务端并接收响应;4. 跨平台需处理Winsock初始化与库链接,编…

    2026年5月10日
    100
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100
  • 三星不再独享,消息称搭载骁龙 8 Gen 3 领先版处理器新机即将发布

    三星不再独享,消息称搭载骁龙 8 Gen 3 领先版处理器新机即将发布三星不再独享,消息称搭载骁龙 8 Gen 3 领先版处理器新机即将发布三星不再独享,消息称搭载骁龙 8 Gen 3 领先版处理器新机即将发布三星不再独享,消息称搭载骁龙 8 Gen 3 领先版处理器新机即将发布

    6 月 15 日消息,据博主@肥威 今日爆料,搭载骁龙 8 Gen 3 领先版%ign%ignore_a_1%re_a_1%的新机即将发布,把之前的 for Galaxy 改成“for Everybody”。 Pic Copilot AI时代的顶级电商设计师,轻松打造爆款产品图片 158 查看详情 …

    2026年5月10日 用户投稿
    100
  • Golang使用Protobuf定义接口与消息格式

    Protobuf通过字段编号实现兼容性,新增字段可忽略、删除字段可保留编号,确保新旧版本互操作,支持服务独立演进。 在Golang项目中,利用Protobuf定义接口和消息格式,本质上是为服务间通信构建了一套高效、类型安全且跨语言的契约。它让数据结构清晰可见,RPC调用标准化,极大地简化了分布式系统…

    2026年5月10日
    000
  • 高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行

    高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行高通预热 2023 骁龙峰会:以AI为主题,10 月 25-26 日举行

    【环球网科技综合报道】10月17日消息,高通今日对 2023 骁龙峰会进行了预热,本次大会将以 %ign%ignore_a_1%re_a_1% 为主题,届时骁龙 8 gen 3 处理器也很大可能在本届峰会亮相。 在临近活动召开之日,相关业内人士也透露了高通骁龙8Gen3跑分及规格。据悉,高通骁龙8 …

    2026年5月10日 用户投稿
    000
  • pycharm解析器怎么添加 解析器添加详细流程

    在pycharm中添加解析器的步骤包括:1) 打开pycharm并进入设置,2) 选择project interpreter,3) 点击齿轮图标并选择add,4) 选择解析器类型并配置路径,5) 点击ok完成添加。添加解析器后,选择合适的类型和版本,配置环境变量,并利用解析器的功能提高开发效率。 在…

    2026年5月10日
    000
  • 硬盘数据被误删除怎么办?教你快速找回删除的文件!

    硬盘数据被误删除,别慌!恢复数据并非不可能,关键在于你接下来的操作。立刻停止对该硬盘的任何写入操作,然后尝试使用专业的数据恢复软件。 解决方案 首先,数据恢复的原理是,删除文件后,操作系统只是将文件占用的空间标记为“可覆盖”,但文件本身的数据可能还存在于硬盘上。所以,避免新的数据写入覆盖掉旧数据,是…

    2026年5月10日
    000
  • Python官网用户调查的参与方式_Python官网反馈提交详细教程

    答案是通过访问Python官网新闻页面、邮件邀请链接或GitHub仓库提交反馈。具体为:访问官网查找用户调查公告,或点击邮件中的专属链接参与,在GitHub的cpython仓库提交技术建议,并注意如实填写问卷与保护隐私。 如果您希望参与Python官网的用户调查并提交反馈,可以通过官方指定的渠道完成…

    2026年5月10日
    000
  • JavaScript Electron桌面应用

    答案:使用JavaScript开发%ignore_a_1%桌面应用需结合Web技术与Node.js,通过主进程管理窗口、渲染进程展示界面,并利用IPC通信,调用系统功能如文件对话框,最后用electron-builder打包发布,注意安全与进程职责分离。 用JavaScript开发Electron桌…

    2026年5月10日
    000
  • Go语言连接外部MySQL数据库:DSN配置与常见错误解析

    本文详细阐述了go语言使用`go-sql-driver/mysql`驱动连接外部mysql数据库的正确方法。重点介绍了数据源名称(dsn)的规范格式,特别是主机地址部分的配置,以避免常见的“getaddrinfow: the specified class was not found.”等网络解析错…

    2026年5月10日
    000
  • C++如何编译和链接_C++从源码到可执行文件的过程解析

    c++kquote>预处理展开宏和头文件,编译生成汇编代码,汇编转为机器码,链接合并目标文件与库生成可执行程序。 当你写完一段C++代码,比如一个简单的hello world程序,最终能运行起来,背后其实经历了一系列步骤:预处理、编译、汇编和链接。这个过程将人类可读的源码转换成机器可以执行的程…

    2026年5月10日
    000
  • Linux文件系统iostat命令使用技巧

    Linux文件系统iostat命令使用技巧Linux文件系统iostat命令使用技巧Linux文件系统iostat命令使用技巧Linux文件系统iostat命令使用技巧

    iostat是Linux系统中用于监控I/O设备负载的关键工具,能分析磁盘性能并识别瓶颈。默认输出包括CPU使用率和设备I/O统计,分为系统启动以来的平均值和当前采样周期数据。核心指标有:%util反映设备利用率,持续接近100%可能表示I/O瓶颈;await为平均I/O等待时间,过高说明响应变慢;…

    2026年5月10日 用户投稿
    000
  • 如何测试html5编码_测试HTML5页面编码兼容性方法【编码测试】

    HTML5页面编码兼容性测试需五步:一查meta charset是否正确且前置;二验HTTP响应头Content-Type charset是否为utf-8;三用file或chardet工具探测实际编码;四跨浏览器测试URL参数中中文、Emoji解析;五通过W3C验证服务检查编码声明与字节一致性。 如…

    2026年5月10日
    100
  • 后缀php怎么打开_php文件打开方式与运行环境搭建指南

    要打开PHP文件需根据用途选择方式:查看代码可用文本编辑器或IDE,运行则需服务器环境。推荐新手使用XAMPP、WAMP等集成环境,将文件放入htdocs目录后访问localhost;开发者可利用PHP内置服务器,命令行执行php -S localhost:8000运行;高级用户可手动配置Apach…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信