25.线程概念和控制(二)

一、线程周边问题

1.线程的优点

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

核心:

与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多:
        最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
        另外⼀个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,⼀旦去切换上下文,处理器中所有已经缓存的内存地址⼀瞬间都作废了。还有⼀个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲 TLB (快表)会被全部刷新,这将导致内存的访问在⼀段时间内相当的低效。但是在线程的切换中,不会出现这个问题,当然还有硬件cache。

2.线程的缺点

性能损失
        一个很少被外部事件阻塞的计算密集型线程往往无法与其它线程共享同⼀个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
健壮性降低
        编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。
缺乏访问控制
        进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
编程难度提高
        编写与调试⼀个多线程程序比单线程程序困难得多。

3.进程和线程对比

进程是资源分配的基本单位。
线程是调度的基本单位。
线程共享进程数据,但也拥有自己的一部分数据:
        线程ID
        一组寄存器,线程的上下文数据(核心)
        栈(核心)
        errno
        信号屏蔽字
        调度优先级
栈说明线程有自己的入口函数,栈存放的是临时变量,说明线程是一个动态的概念。
一组寄存器说明线程是被独立调度的。
进程的多个线程共享:
        同⼀地址空间,因此Text Segment、Data Segment都是共享的,如果定义⼀个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
        文件描述符表
        每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
        当前工作目录
        用户id和组id

二、线程控制

ps -aL可以用来查看用户创建的线程。

task_struct

{

        pid_t pid;

        pid_t lwp;

}

light weight process:轻量级进程。CPU调度的时候,看lwp。

线程相关子问题:

1.关于调度的时间片问题:等分给不同的线程(防止线程无限分裂)

2.异常之后?任何一个线程崩溃,都会导致整个进程崩溃!

3.线程库pthread是用户层和操作系统层之间的一层中间层,在编译时需要指定动态链接,带上 -lpthread

        Linux中,不存在真正意义上的线程,它所谓的概念,使用轻量级进程模拟的。在OS中,只有轻量级进程,所谓的线程,只是用户层的概念。

        但用户层只认线程,因此pthread库诞生了,把创建轻量级进程封装起来,给用户提供一批创建线程的接口。

         linux的线程实现,是在用户层实现的。我们称之为:用户级线程。pthread为原生线程库。

拓展:

linux创建轻量级进程的接口,vfork和clone创建子进程,和父进程共享地址空间。

C++11的多线程,在linux下,本质是封装了pthread库。

语言为了其跨平台可移植性,其对应的线程实现都是封装了各个操作系统的系统调用或对应的库,通过条件编译,形成库。

int pthread_create(pthread_t *thread,  const pthread_attr_t *attr,

                                void *(*start_routine)(void *),  void *arg);

thread:线程id,输出型参数(注:这个线程id是线程库的概念,和lwp不同)

attr:线程属性,设置称nullptr

start_routine:新线程要执行的函数入口,一个函数指针

arg:新线程要执行的函数入口的参数

返回值:成功返回0,失败返回错误数字。

注:线程创建好之后,新线程要被主线程等待->类似僵尸进程的问题,内存泄漏。

int pthread_join(pthread_t thread, void **retval);

thread:线程id

retval:返回值指针,输出型参数

pthread_t pthread_self(void);    //获取当前线程的id

为什么retval没有信号部分的内容(没有异常相关的内容)?

    等待的目标线程,如果异常了,整个进程都退出了,包括main线程,所以,join异常,没有意义,看也看不到!join都是基于:线程健康跑完的情况,不需要处理异常信号,异常信号,是进程要处理的话题!!!

POSIX线程库
1.线程终止
1)线程的入口函数,进行return就是线程终止
注意:线程不能用exit终止,因为exit是终止进程的
2)pthread_exit()
3)如果线程被取消,退出结果是-1(PTHREAD_CANCLED)

pthread_exit函数
功能:线程终⽌
原型:
void pthread_exit(void *value_ptr);
参数:
value_ptr:value_ptr不要指向⼀个局部变量。
返回值:
无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
        需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的, 不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
pthread_cancel函数
功能:取消⼀个执⾏中的线程
原型:
int pthread_cancel(pthread_t thread);
参数:
thread:线程ID
返回值:成功返回0;失败返回错误码
特别注意:取消的时候一定要保证新线程已经启动。

2.线程分离

如果主线程不想再关心新线程,而是当新线程结束的时候,让他自己释放??
设置新线程为分离状态

技术层面:  线程默认是需要被等待的,joinable。如果不想让主线程等待新线程
想让新线程结束之后,自己退出,设置为分离状态(!joinable or detach)  // TODO
理解层面:线程分离,主分离新,新把自己分离。
分离的线程,依旧在进程的地址空间中,进程的所有资源,被分离的线程,依旧可以访问,可以操作。主不等待新线程。
如果线程被设置为分离状态,不需要进行join,join会失败!!

#include <iostream>
#include <pthread.h>
#include <string>
#include <cstring>
#include <unistd.h>void *routine(void *args)
{std::string name = (char *)args;while (true){sleep(1);std::cout << "I am a thread, name: " << name << std::endl;}return (void *)123;
}int main()
{pthread_t tid;int n = pthread_create(&tid, nullptr, routine, (void *)"thread-1");// 分离新线程pthread_detach(tid);sleep(10);// 取消新线程pthread_cancel(tid);// 等待新线程,获取返回值void *retval = nullptr;int ret = pthread_join(tid, &retval);if (ret != 0)std::cout << "等待失败," << strerror(ret) << std::endl;elsestd::cout << "等待成功" << std::endl;while (true){std::cout << "当前是主线程,新线程已被回收,退出信息为:" << (long long int)retval << std::endl;sleep(1);}return 0;
}

三、线程ID及地址空间布局

        Linux中没有真正意义上的线程 -> OS不会直接提供线程的接口 -> 在用户层,封装轻量级进程,形成原生线程库 -> 动态库,ELF格式 -> 加载到内存和映射到进程地址空间。

        线程的概念是在库中维护的,在库内部,一定会事先存在一个或多个被创建好的线程,如何管理这些线程?先描述,在组织。

struct tcb

{

        // 线程应有的属性

        // 线程状态

        // 线程ID

        // 线程独立的栈结构

        // 线程栈大小

        ... ...

};

        为什么不包括上下文,lwp,优先级?因为这是linux系统内核的具体实现,不是操作系统学科对于线程的描述,用户不需要关心具体实现。

        进程自己的代码区可以访问到pthread库内部的函数和数据。

线程ID本质:管理线程块的起始虚拟地址。

线程传参和返回值:保存在线程管理块中

线程分离:线程管理块中存在标识位,标识线程是否分离。

以下是 glibc-2.4 pthread 源码相关内容:
路径: nptl/pthread_create.c

线程tcb结构属性:

更正tid为lwp,轻量级进程的id。

创建线程create_thread

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

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

相关文章

【CVPR2023】奔跑而非行走:追求更高FLOPS以实现更快神经网络

文章目录一、论文信息二、论文概要三、实验动机四、创新之处五、实验分析六、核心代码注释版本七、实验总结一、论文信息 论文题目&#xff1a;Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks中文题目&#xff1a;奔跑而非行走&#xff1a;追求更高FLOPS…

JVM(二)--- 类加载子系统

目录 前言 一、类加载过程 1. loading阶段 2. Linking阶段 2.1 验证 2.2 准备 2.3 解析 3. Initialization阶段 二、类加载器 1. 类加载器的分类 2. 用户自定义类加载器 三、双亲委派机制 四、其他知识点 前言 JVM的内存结构如图所示&#xff1a; 一、类加载过程…

Docker 容器的使用

1.容器的基本信息[roothost1 ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9ac8245b5b08 img-layers-test "python /app/app.py" 45 hours ago Exited (0) 45 hour…

LLMs之Hallucinate:《Why Language Models Hallucinate》的翻译与解读

LLMs之Hallucinate&#xff1a;《Why Language Models Hallucinate》的翻译与解读 导读&#xff1a;该论文深入分析了语言模型中幻觉现象的成因&#xff0c;认为幻觉源于预训练阶段的统计压力和后训练阶段评估体系对猜测行为的奖励。论文提出了通过修改评估方法&#xff0c;使其…

Spring Cloud @RefreshScope 作用是什么?

RefreshScope 是 Spring Cloud 中的一个重要注解&#xff0c;主要作用如下&#xff1a; 主要功能动态刷新配置 使 Bean 能够在运行时动态刷新配置属性当配置中心的配置发生变化时&#xff0c;无需重启应用即可生效作用域管理 为 Bean 创建一个特殊的作用域 refresh标记的 Bean …

Flutter SDK 安装与国内镜像配置全流程(Windows / macOS / Linux)

这是一份面向国内网络环境的 Flutter 从零到可运行指引&#xff1a;覆盖 SDK 安装、平台依赖准备、国内镜像配置&#xff08;PUB_HOSTED_URL、FLUTTER_STORAGE_BASE_URL&#xff09;、Android 侧 Gradle 仓库加速&#xff0c;以及 Java/Gradle 版本兼容的关键坑位与排查思路。文…

【Java】NIO 简单介绍

简介 从 Java 1.4 版本开始引入的一个新的 I/O API&#xff0c;可以替代标准的 Java I/O。提供了与标准 I/O 不同的工作方式&#xff0c;核心是 通道&#xff08;Channel&#xff09;、缓冲区&#xff08;Buffer&#xff09; 和 选择器&#xff08;Selector&#xff09;。支持非…

Java爬虫获取京东item_get_app数据的实战指南

一、引言京东开放平台提供了丰富的API接口&#xff0c;其中item_get_app接口可用于获取商品的详细信息。这些数据对于市场分析、价格监控、商品推荐等场景具有重要价值。本文将详细介绍如何使用Java编写爬虫&#xff0c;通过调用京东开放平台的item_get_app接口获取商品详情数据…

Vue3源码reactivity响应式篇之批量更新

概述 在vue3响应式系统设计中&#xff0c;批量更新是优化性能的核心机制之一。当短时间内频繁多次修改响应式数据时&#xff0c;批量更新可以避免频繁触发订阅者的更新操作&#xff0c;将这些更新操作合并为一次&#xff0c;从而减少不必要的计算和DOM操作。 批量更新也是利用链…

AI 模型训练过程中参数用BF16转向FP16的原因

大模型训练从 FP16 转向 BF16 是一个关键的技术演进&#xff0c;其核心原因在于 BF16 在动态范围和精度之间取得了更优的平衡&#xff0c;从而极大地提升了训练&#xff08;尤其是大模型训练&#xff09;的稳定性和有效性。 1. 背景 为什么需要半精度浮点数 (FP16)&#xff1f;…

python网络爬取个人学习指南-(五)

**************************************************************************************************************author&#xff1a;keyinfodate&#xff1a;2025-09-09 23:50title&#xff1a;网络信息爬取之多联级标题内容点击****************************************…

RAG - 检索增强生成

第一部分&#xff1a;RAG 详解一、RAG 是什么&#xff1f;RAG&#xff08;Retrieval-Augmented Generation&#xff0c;检索增强生成&#xff09;是一种将信息检索&#xff08;或知识检索&#xff09;与大语言模型&#xff08;LLM&#xff09;的生成能力相结合的技术框架。它的…

大数据毕业设计选题推荐-基于大数据的分化型甲状腺癌复发数据可视化分析系统-Spark-Hadoop-Bigdata

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Spring Bean扫描

好的&#xff0c;没有问题。基于我们之前讨论的内容&#xff0c;这是一篇关于 Spring Bean 扫描问题的深度解析博客。Spring Bean扫描作者&#xff1a;Gz | 发布时间&#xff1a;2025年9月9日&#x1f3af; Spring如何找到你的Bean&#xff1f; 首先要理解原理。Spring的组件扫…

【 运维相关】-- HTTP 压测/负载发生器之新秀 oha

目录 oha 项目分析&#xff08;hatoo/oha&#xff09; 一、概述 二、安装 三、快速上手 三、常用参数&#xff08;摘选&#xff09; 四、输出解读&#xff08;终端 TUI&#xff09; 五、与其它工具对比 六、最佳实践 七、注意事项 八、参考 oha 项目分析&#xff08;h…

淘宝闪购基于FlinkPaimon的Lakehouse生产实践:从实时数仓到湖仓一体化的演进之路

摘要&#xff1a;本文整理自淘宝闪购(饿了么)大数据架构师王沛斌老师在 Flink Forward Asia 2025 城市巡回上海站的分享。引言在数字化转型的浪潮中&#xff0c;企业对实时数据处理的需求日益增长。传统的实时数仓架构在面对业务快速变化和数据规模爆炸性增长时&#xff0c;逐渐…

Android应用添加日历提醒功能

功能 在安卓应用里调用系统日历&#xff0c;直接创建一个带提醒的日历事件&#xff0c;甚至不需要跳转到日历界面&#xff0c;只需要获取系统日历的读取权限即可。 需要的权限 在AndroidManifest.xml里添加 <uses-permission android:name"android.permission.READ_CAL…

‌Git Bisect 二分查找定位错误总结

# Git Bisect 二分查找指南## 1. 基本原理&#xff08;ASCII示意图&#xff09; 假设提交历史是一条时间线&#xff0c;Ggood&#xff08;正常&#xff09;&#xff0c;Bbad&#xff08;异常&#xff09;&#xff1a;提交顺序: G --- G --- G --- B --- B --- B | | | 初始正常…

ThingsKit物联网平台 v2.0.0 发布|前端UI重构、底层架构升级

v2.0.0 Release发布日期&#xff1a;2025/08/25 代码标签&#xff1a;v2.0.0_Release&#x1f947; 新增功能国标级联&#xff08;支持上级、下级国标级联&#xff09;视频回放、录像计划&#xff08;用户可以通过录像计划生成对应的视频回放并查看&#xff09;Modbus_TCP协…

Lua > Mac Mini M4安装openresty

Mac Mini M4安装openresty 主要参考 https://www.cnblogs.com/helios-fz/p/15703260.html brew uninstall nginxbrew update brew install pcre openssl #brew install geoip# brew tap openresty/brew # brew install openresty # brew install openresty/brew/openresty# VER…