【Linux系统编程二十六】:线程控制与线程特性(Linux中线程库/线程创建/线程退出/线程等待)

【Linux系统编程二十六】:线程控制与线程特性

  • 一.Linux线程库pthread
    • 1.线程控制块
    • 2.线程tid
    • 3.线程栈
  • 二.线程控制
    • 1.线程创建
    • 2.线程退出
    • 3.线程等待
  • 三.线程的特性
    • 1.独立栈
    • 2.局部存储
    • 3.线程可分离

一.Linux线程库pthread

在Linux中,是没有明确的线程概念的,因为在内核里线程是用进程的内核数据结构模拟实现的,只有轻量级进程的概念。所以在内核里它没有直接控制线程的接口,只有通过轻量级进程的系统调用:
在这里插入图片描述

简单介绍一下该系统调用,第一个参数就是一个函数指针,是该线程要执行的方法函数。第二个参数是一个栈指针,指向的是一块栈空间。至于为什么要栈,后面会讲到。

而我们用户只想要线程,不想要所谓的轻量级进程,所以就有人在应用层给我封装了一个线程库pthread。
它内部就是封装了调用轻量级进程系统调用接口,然后给我封装出一批创建线程,退出线程,等待线程的接口。
所以我们想使用Linux中的线程,需要使用第三方pthread库。
在这里插入图片描述

1.线程控制块

我们要理解的是,操作系统内部是没有线程的,只有轻量级进程,线程是在用户层创建出来的。是用户层维护线程的,所以叫做用户级线程。

因为库是共享的,你在库中可以创建线程,我也可以创建线程,所以线程库中会存在很多的线程,这些线程需不需要管理呢?当然需要!
所以线程库需要维护线程,要管理线程—先描述,再组织。
在线程库中其实是存在一个叫做线程控制块的结构体,它就是用来描述线程的各种属性比如线程的字段,线程的独立栈,线程的回调函数等。
在这里插入图片描述

创建一个线程,线程库就需要开辟一块空间,用来创建线程的TCB。
这是在用户层面,而线程库使用时,是需要加载到内存面的,在内核层面,没有所谓的TCB(线程控制块),它只有一个执行流,也就是轻量级进程,对于操作系统来说,它管理的是执行流。每一个线程在内核里都有唯一的LWP。

2.线程tid

根据上面的理解,我们知道在用户层面,线程库会为线程创建线程控制块TCB。那么这么多线程,用户是如何区别这些线程的呢?每一个线程在用户层都会存在一个tid,用来区别不同线程的。根据动态库的学习,我们知道,在链接时需要将动态库加载到内存的共享区里。
在这里插入图片描述
而线程库链接时也需要加载到内存的共享区里。
线程库里存在很多描述线程的线程控制块TCB。线程库按照数组的方式将各个线程控制块管理起来。所有的线程都在动态库里被管理着。
而这些线程控制块在共享区里的起始地址就就是该线程的tid。
因为为了更好的找到每个TCB在共享库里的位置,每一个TCB在内存的起始地址就称为线程的tid
在这里插入图片描述
在这里插入图片描述

所以线程的tid本质就是共享区内存的一个虚拟地址。

线程获取自己的tid,系统调用接口:
在这里插入图片描述

3.线程栈

线程控制块里还存在一个线程栈,这是什么,又为什么呢?
因为每一个线程都是一个独立的执行流,各自在不同的地址空间执行代码,也就是执行不同的函数。而函数里难免会需要各种临时变量,或者调用其他函数,传递形参等,这些都需要栈空间存储。所以每一个线程创建时,都需要一块栈空间,用来维护各自函数里临时变量。
而主线程用的是地址空间里的栈空间。其他线程用的都是共享区里开辟的空间。在线程创建时,首先在上层线程库会为新线程创建一堆数据结构叫做线程控制块,里面维护着该线程的tid,该线程的栈空间等。上层将tcb创建好后,在内核层面就会形成对应的执行流,然后调用系统调用clone,将线程的栈空间,传递给child_stack。
在这里插入图片描述
在这里插入图片描述

二.线程控制

1.线程创建

main函数执行流我们称为是主线程,主线程创建的线程我们称为新线程。
在这里插入图片描述

1.pthread_create()是用来创建线程的。它会产生一个线程id,用来标识该线程的唯一性。线程的后续操作,都是根据该线程的id来操作线程。该id的本质就是共享区内存的虚拟地址。(就是线程控制块TCB在共享区内存的起始地址)
2.pthread_t是操作系统提供的数据类型,类似于int
3.第一个参数是用作输出型参数的,它是将创建的线程的tid给带出来。
4.第二个参数,是用来控制线程的属性,不用的情况下,置nullptr就可以。
5.第三个参数是一个函数指针,它是该线程要执行的方法函数。该函数的返回值和参数都是void类型。
6.第四个参数,是新线程执行的方法函数的参数。执行线程函数也可能需要参数,所以这个参数,就是主线程传给新线程的参数。主线程想让子线程知道什么,就可以通过该参数传递给子线程。该参数是一个void
类型,不过注意不仅可以传普通类型过去还可以传对象过去。
7.
注意在编译的时候需要链接线程库。
在这里插入图片描述

创建的线程和主线程是如何区分的呢?
在这里插入图片描述
根据LWP来区别,或者根据它们的tid来区别。
任何一个线程被干掉,进程就会被干掉,因为线程就是进程内部的一个执行分支。

2.线程退出

线程执行的函数返回了,就代表线程退出。
线程退出有两种方式,一种是函数之间return返回退出。
一种是使用系统调用pthread_exit()来退出。
注意不能使用exit()接口,它是用来进程退出的:
在这里插入图片描述

注意线程退出,返回的是void*类型,如果没有什么可以退出的,就直接返回nullptr;
形参可以接收一个对象指针,那么最后函数返回时也可以返回一个对象。
在这里插入图片描述

3.线程等待

主线程创建新线程,也需要观看这个新线程,需要等待新线程。不然会出现类似于"僵尸进程"。导致资源泄露。并且有时主线程也需要获取创建的线程的一些处理结果。所以需要主线程最后退出。

在这里插入图片描述

1.pthread_join()是用来进程等待的。
2.第一个参数就是线程的tid,创建线程时,会将线程的tid给带出来。
3.第二个参数retval是一个输出型参数,是用来将线程执行函数结果给带出来,因为线程执行的函数的返回值是void类型,要想将该类型带出来,必须使用指针类型,也就是void *类型。
在这里插入图片描述4.主线程在等待时,默认是阻塞等待,如果线程不退出,那么主线程就会一直等待。
5.线程退出时,主线程join等待不需要考虑异常情况,因为做不到,只有进程才可以做到。
6.一般来说,主线程退出,其他线程也会退出,但是如果使用pthread_exit(主线程tid),其他线程可能不会退出。

主线程还可以取消新线程(该线程已经被创建完毕)
在这里插入图片描述
线程如果被取消,那么它执行的函数返回值就会为-1。

三.线程的特性

因为在内核里没有线程的具体实现,线程是在用户层利用第三方库实现的,所以在内核中是如何区分线程和进程的呢?在内核中是没有线程和进程概念,它们都是轻量级进程,而每一个轻量级进程都有唯一标识它的LWP(Light Weight Process)。
而与之对应的用户层是真正实现了线程,所以叫做用户级线程,每一个线程都具有自己的tid。
所以这样对应起来,真正的线程是具有两部分构成的,一个是上层用户级线程,一个是下层内核级线程。用户级执行流:内核LWP=1:1

1.独立栈

每一个线程都是具有独立的栈结构的,当不同的线程调用同一个函数时,虽然是同一个函数,但是在不同的函数栈帧中。所以同一个变量,是在不同的栈帧中开辟的,地址也就不会相同。在这里插入图片描述

但这个独立是相对独立,不是真的独立私有,其他线程想要访问你的栈也是可以访问到的,因为这些线程都是在同一个地址空间上运行,所以在线程中是没有秘密可言的,只不过线程之间最好不要越界访问。
在这里插入图片描述

2.局部存储

全局变量是每一个线程都可以访问的,就相当于一个共享资源。访问到的都是同一个。
在这里插入图片描述
那么有没有这样的变量,它是线程独有的,但又是全局的呢?也就是线程想要一个私有的全局变量呢?

线程的局部存储可以满足这样的条件。什么叫局部存储呢?
在这里插入图片描述
在线程控制块里,存储着一个叫线程局部存储的对象。它也是一块空间,是在共享区内存提前申请好的,当线程创建时,就已经存在。也就是每一个线程都会有一个局部存储,它可以用来定义全局变量,但这个全局变量又只有该线程能访问。
可以将线程自己不变的属性,且频繁调用的,放进去。然后再交给线程。

可能有人会疑惑,为什么要有局部存储,为什么不在线程函数内部直接设置一个临时变量不就好啦。
这样做的原因很简单,效率高,不需要频繁的创建变量,不需要传递参数也可以使用。

3.线程可分离

主线程创建新线程后,有两种情况,一种是等待新线程,一种是不等待线程。
等待线程,可能需要获取新线程的返回结果。
不等待线程,说明不关心新线程的返回结果。
在这里插入图片描述

但是你又说了,不等待线程会造成资源泄露。
有一种方法可以让主线程,不等待新线程,也不会造成资源泄露:线程分离

也就是主线程与新线程分离后,主线程不关心新线程的返回结果,新线程退出后会自动释放资源。

int pthread_detach(pthread_t thread);
//thread就是线程的tid

线程分离有两种方法:
①主线程强制分离
在主线程里,利用创建线程获取得到的tid,来分离新线程。
②新线程主动分离
在新线程执行的函数里,利用(pthread_self()获取自己的tid来分离。

在这里插入图片描述

【注意】
①一个线程被分离后,主线程就不能再等待它了。如果该线程分离后,主线程又等待它,这样会报错。
②分离线程,说明主线程是不关心该线程的返回结果,也就是不想等待该线程,但还是要保证主线程,最后退出。
在这里插入图片描述

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

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

相关文章

19个地信专业可以投的岗位汇总【GIS求职秘籍】

今天给大家详细科普一下,GIS专业的同学毕业以后还能从事哪些岗位的工作。 (这期不包含学校老师等事业编岗位) 一、GIS数据采集和处理 GIS数据采集和处理在这里分为一个大类,包含前期测绘外业的实地采集,后续的数据加…

石化行业设备管理系统的作用

石化行业是全球经济中不可或缺的重要组成部分,它涵盖了石油、天然气、化工等领域。在这个高风险和高安全要求的行业中,设备的可靠性和安全性至关重要。为了有效管理和维护设备,石化公司越来越多地采用设备管理系统,以提高生产效率…

MongoDB—SQL到MongoDB映射图表

一、术语和概念 下表显示了各种 SQL 术语和概念 以及相应的 MongoDB 术语和概念。 SQL Terms/Concepts MongoDB Terms/Concepts database database table collection row document or BSON document column field index index table joins $lookup, embedded docu…

CSS 伪类函数 :is() 和 :where()

在编写 CSS 时,有时可能会使用很长的选择器列表来定位具有相同样式规则的多个元素。例如,如果您想对标题中的 b 标签进行颜色调整,我们应该都写过这样的代码: h1 > b, h2 > b, h3 > b, h4 > b, h5 > b, h6 > b…

不想root,但想远程控制vivo手机?这个方法不用root也能做到

远程控制vivo手机不用root!今天给大家讲讲免Root情况下,笔记本电脑如何远程控制vivo手机。 在电脑和手机都安装AirDroid,这是免Root的关键。 下载AirDroid个人版 | 远程控制安卓手机软件下载下载AirDroid个人版进行文件传输和管理、远程控制安…

八、HTML 链接

一、HTML 链接 HTML 使用超级链接与网络上的另一个文档相连。 HTML中的链接是一种用于在不同网页之间导航的元素。 链接通常用于将一个网页与另一个网页或资源(如文档、图像、音频文件等)相关联。 链接允许用户在浏览网页时单击文本或图像来跳转到其…

Python进程、线程、协程:多任务并发编程指南

概要 在当今计算机时代,为了提高程序的性能和响应速度,多任务并发编程成为了一种必不可少的技术手段。而Python作为一门高级编程语言,提供了多种多任务并发编程的方式,包括进程、线程和协程。本文将详细介绍这三种方式的使用教程…

Unity 打包AB 场景烘培信息丢失

场景打包成 AB 资源的时候,Unity 不会打包一些自带相关的资源 解决办法:在 Project settings > Graphics下设置(Automatic 修改成 Custom)

基于web的电影院购票系统

**🍅点赞收藏关注 → 私信领取本源代码、数据库🍅 本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目希望你能有所收获,少走一些弯路。🍅关注我不迷路🍅**一 、设计说明 1.1选题动因 …

cnPuTTY 0.80.0.1—PuTTY Release 0.80中文版本简单说明~~

2023-12-18 官方发布了PuTTY 0.80本次发布主要是针对Terrapin攻击(CVE-2023-48795)的修改发布。 更多详细的内容请查看PuTTY Change Log。 有关Terrapin攻击可用简单参考:警告!!!Terrapin攻击(CVE-2023-48795)~~~ 为了缓解此漏洞…

振弦采集仪在地基沉降监测中的应用研究

振弦采集仪在地基沉降监测中的应用研究 振弦采集仪是一种专门用于测量地基沉降的仪器,它采用振弦原理来测量地基的沉降情况。振弦采集仪通过在地基上安装一根细长的弹性振弦,并测量振弦的变形来获得地基沉降的数据。在地基沉降监测中,振弦采…

TypeScript Array(数组)

目录 1、数组初始化 2、Array 对象 3、数组迭代 4、数组在函数中的使用 4.1、作为参数传递给函数 4.2、作为函数的返回值 5、数组方法 数组对象是使用单独的变量名来存储一系列的值。数组非常常用。假如你有一组数据(例如:网站名字)…

从“五力”看百亿西凤的必然性

执笔 | 文 清 编辑 | 萧 萧 2023年末,西凤成功突破市场阻碍、跑赢行业周期,正式跻身中国百亿白酒品牌阵容。这是一份全行业及全体西凤人“预期之内”的成绩单。 当下,中国白酒已经进入“存量竞争”时代,马太效应使得强者恒强…

8-链表-旋转链表

这是链表的第8篇算法,力扣链接。 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3]示例 2: 输入:h…

[足式机器人]Part2 Dr. CAN学习笔记-Ch01自动控制原理

本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记-Ch01自动控制原理 1. 开环系统与闭环系统Open/Closed Loop System1.1 EG1: 烧水与控温水壶1.2 EG2: 蓄水与最终水位1.3 闭环控制系统 2. 稳定性分析Stability2.1 序言2.2 稳定的分类2.3 稳定的对…

【PTA-C语言】编程练习5 - 函数与指针

如果代码存在问题,麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 编程练习5 - 函数与指针 6-1 求实数和的函数(分数 10)6-2 求解一元二次方程实根的函数(分数 10)6-3 求集合数据的均方差(分数 10)6-4 计…

天融信TOPSEC Cookie 远程命令执行漏洞

产品介绍 天融信TopSec 安全管理系统,是基于大数据架构,采用多种技术手段收集各类探针设备安全数据,围绕资产、漏洞、攻击、威胁等安全要素进行全面分析,提供统一监测告警、集中策略管控、协同处置流程,实现客户等保合…

FindMy技术用于鼠标

鼠标是计算机的标准配置之一,其设计初衷是为了使计算机的操作更加简便快捷,减少用户在操作中的负担。用户可以通过移动鼠标,实现光标的精确移动,进而选择、拖拽、复制、粘贴等操作。这种操作方式,使得计算机的操作变得…

人工智能未来发展前景怎么样?

人工智能的未来发展前景怎么样?人工智能的未来发展前景非常广阔,有以下几个方面的发展趋势和前景: 1、人工智能的未来发展前景-应用领域扩展:人工智能将在各个领域得到广泛应用,包括医疗保健、金融、交通、制造业、农业…

<软考高项备考>《论文专题 - 45 范围管理(4) 》

5 过程4-创建WBS 5.1 问题 4W1H过程做什么把项目可交付成果和项目工作分解成较小、更易于管理的组件的过程作用:为所要交付的内容提供0架构为什么做WBS代表着项目范围说明书所规定的工作,可以针对WBS 的工作包安排进度,估算成本和实施监控谁…