有专门学做衣服网站有哪些/网络优化初学者难吗
文章目录
- 线程的基础概念
- 线程控制
- 内核LWP和线程ID的关系
线程的基础概念
一般教材对线程描述是:是在进程内部运行的一个分支(执行流),属于进程的一部分,粒度要比进程更加细和轻量化
一个进程中是可能存在多个线程的,可能是1:1也可能是1:n,所以线程也应由OS管理,管理就离不开“先描述,再组织”,所以线程也应该类似PCB一样有线程控制块TCB,但Linux中没有专门为线程设计的TCB,而是用进程PCB来模拟线程
通过某些函数方法,可以在同一个进程中只创建task_struct,让这些PCB共享同一个地址空间,把当前进程的资源(代码+数据)划分成若干份,让每个PCB使用执行一部分。
站在CPU的角度,CPU只看PCB,不关心是否共享一份地址空间,此时CPU看到的PCB就是一个需要被调度的执行流
Linux中是用进程来模拟线程的,所以也叫轻量级进程(站在用户角度是线程),不用单独为线程设计算法,可直接用进程的一套相关方法,所以不用维护复杂的进程和线程的关系。OS只需要聚焦在线程间的资源分配上就可以了,所以CPU是只看PCB,不区分线程和进程。
进程与线程的区别:
-
进程具有独立性,可以有部分资源共享(基于管道、ipc资源下)
-
线程共享进程的数据,但是也有属于自己的数据
-
进程是资源分配的基本单位,承担系统资源分配的基本实体
-
线程是CPU调度的基本单位,承担进程资源一部分的基本实体
线程的优点:
-
创建一个新的线程的代价比创建一个进程小得多。创建一个线程虽然也需要创建数据结构,但是并不需要重新开辟资源,只需要将进程的部分资源分配给线程。创建一个进程不仅需要创建大量数据结构,还需要重新创建资源。
-
与进程之间的切换相比,线程之间的切换需要操作系统做的工作少。线程只是进程的部分资源,切换的资源少。
-
线程占用的资源比进程少
-
能充分利用多处理器的可并行数量
-
在等待慢速的I/O任务结束的同时,程序可以执行其它的计算任务
-
计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现。
-
I/O密集型应用,为了提高性能,将I/O操作重叠,线程可以同时等待不同的I/O操作。I/O操作是与外设交互数据,会很慢。
线程的缺点:
-
性能缺失
一个处理器只能处理一个线程,如果线程数比可用处理器数多,会有较大的性能损失,会增加额外的同步和CPU调度的开销,而资源却是不变的 -
健壮性降低
编写多线程时, 可能因为共享的变量, 导致一个线程修改此变量, 影响另外一个线程。(线程不是对立的,所以线程之间缺乏保护) (多线程之间的变量是同一个变量;多进程之间变量不是同一个变量,一开始是共享父进程的,但在改变时发生写时拷贝)
-
缺乏访问的控制
进程时访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响 -
编程难度相对高
线程异常:
- 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃 ;线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,该进程内的所有线程也就随即退出
线程用途:
- 合理的使用多线程,能提高CPU密集型程序的执行效率
- 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是 多线程运行的一种表现)
线程与进程的关系
线程控制
因为是用进程模拟的线程,所以Linux下不会给我们提供直接操作线程的接口,而给线程提供的是在同一个地址空间创建PCB的方法、分配资源给指定的PCB的接口等等,需要用户自己创建一些函数方法如创建线程、释放线程、等待线程等,对用户不太友好。
所以一般我们在使用的线程相关函数方法,都是由系统级别的工程师在用户层对Linux轻量级进程提供的方法进行封装打包成的库。
下面主要介绍pthread库
- 创建线程函数
- threaad:输出型参数,会获取刚创建的线程ID
- attr:传入的是线程的属性(优先级、运行栈等等)一般传入NULL,交给OS默认处理。
- start_routine:创建线程后所执行的方法
- arg:一般会传给第三个参数的函数方法
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>void* ThreadRun(void* args)
{std::string name = (char*)args;while(true){std::cout << "当前处于新线程,新线程ID是:" << pthread_self() << ", args: " << name << std::endl;sleep(1);}
}int main()
{pthread_t tid[5];for(int i = 0;i < 5;i++)pthread_create(&tid[i],nullptr,ThreadRun,(void*)"new thread");while(true){std::cout << "当前处于主线程,主线程ID是:" << pthread_self() << std::endl;std::cout << "###################################################################" << std::endl;for(size_t i = 0;i < 5;i++)std::cout << "创建的新线程[" << i << "]ID是:" << tid << std::endl;std::cout << "###################################################################" << std::endl;sleep(3);}return 0;
}
犹豫是多线程打印,会有乱码现象。
代码中用到的pthread_self()
,作用是在谁的线程下用,就获取谁的线程 ID
可以看到的是,他们的PID都是一样的,所以线程是对进程的资源分配 ,LWP是内核提供的,和打印出来的id是不一样的,这里用的是原生线程库,打出来的id是其实是进程地址号
- 线程等待
进程退出有三种方式
- 代码正常运行,结果正确
- 代码正常运行,结果不对
- 程序异常,直接退出
前两点退出时不会产生信号,但会产生退出码,而程序异常会产生终止信号;线程分配的资源是进程的一部分,所以只要有一个线程执行的代码部分导致程序崩溃,整个进程会直接退出并产生终止信号,整个进程崩溃,所有线程也就崩溃了。
一般而言,线程也是需要等待的,否则也会出现类似僵尸进程那样的问题,已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,占用资源还得维护数据
线程等待只能是在代码正常运行的前提下等待,而程序异常,进程就会直接退出了,OS会向进程发送退出信号,此时与线程无关,下图所示线程等待函数:
-
thread:需要等待的线程的ID
-
retval:输出型参数,获取创建线程时指定的函数方法的返回值,(因为指定的函数方法返回值是指针返回,所以这里是二级指针)
pthread_join函数在等待时是阻塞式等待
#include <iostream>
#include <string>
#include <pthread.h>
#include <unistd.h>void* ThreadRun(void* args)
{sleep(3);return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");void* status = NULL;pthread_join(tid,&status);std::cout << (char*)status << std::endl;return 0;
}
-
线程退出
线程退出的方法有三种
-
函数中return
-
main函数return代表主线程和进程退出
-
其他线程函数中return,代表当前线程退出
-
-
通过pthread_exit函数终止线程
参数是要传入的退出状态
void* ThreadRun(void* args)
{std::string name = (char*)args;std::cout << name << " running. . ." << std::endl;sleep(3);pthread_exit((void*)123);std::cout << "exit fail" << std::endl;return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return 0;
}
-
通过pthread_cancel函数取消线程
只需要传入要取消的线程的id即可
成功返回0,失败返回-1
void* ThreadRun(void* args) {std::string name = (char*)args;std::cout << name << " running. . ." << std::endl;sleep(10);return (void*)"finish"; }int main() {pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"new thread");sleep(1);std::cout << "cancel sub thread. . ." << std::endl;sleep(5);pthread_cancel(tid);void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return 0; }
取消后的线程退出码是 -1 代表是PTHREAD_ CANCELED的意思
注意:不建议在子线程中取消主线程的做法,这样会导致进入僵尸进程的状态。因为主线程会退出而没有资源可以等待子线程退出,子线程就造成资源浪费了
void* ThreadRun(void* args)
{sleep(5);pthread_cancel((pthread_t)args);while(1){std::cout << " running. . ." << std::endl;sleep(1);}return (void*)"finish";
}int main()
{pthread_t tid;pthread_t g_tid = pthread_self();pthread_create(&tid,nullptr,ThreadRun,(void*)g_tid);void* status = NULL;pthread_join(tid,&status);printf("%d\n",(int*)status);return 0;
}
如图所示:进入了僵尸进程,以及类似僵尸进程的线程状态(主线程 defunct)
- 线程分离
若不想阻塞式的等待线程退出,可以使用线程分离函数;分离之后的线程不需要被join,运行完毕后会自动释放资源
传入要分离的线程id即可
void* ThreadRun(void* args)
{pthread_detach(pthread_self());std::cout << " running. . ." << std::endl;sleep(1);return (void*)"finish";
}int main()
{pthread_t tid;pthread_create(&tid,nullptr,ThreadRun,(void*)"hello");sleep(3);void* status = NULL;int ret = pthread_join(tid,&status);printf("ret:%d,status:%s\n",ret,(char*)status);return 0;
}
只要线程分离了,就不可在使用join去等待了,会等待失败
内核LWP和线程ID的关系
我们使用的pthread原生线程库是存在磁盘当中的,在使用时会被加载到内存当中,通过页表映射到进程地址空间的共享区(堆区和栈区之间的区域)所有的进程都共用同一份库(只是进程空间中进程自己认为独占这个库)
而每个线程共享的是同一份进程地址空间,但是每个线程也会有自己的私有数据部分如线程栈(主线程栈是在栈区的)、上下文数据、属性等等。这些线程私有的部分组成线程的数据块(可以想象成类似于task_struct的线程struct),数据块其实是存储在线程库中的,通过页表映射到进程地址的共享区。
(创建线程的时候,用mmap系统调用从heap分配出来的)
在使用pthread_create创建线程时,线程库会生成一个ID号来表示新创建的线程,这个ID由函数的第一个参数可以获取到;而线程ID就是这些数据块在共享区映射的地址,每个线程id就是每个线程的数据块在共享区的地址,通过id号便能找到线程的所有数据
线程id就是:线程存储在线程库中的数据,通过内存映射到进程地址空的共享区的地址标号,有了这个标号就能找到线程的所有的私有数据
LWP和线程id的关系是什么?
LWP是在内核层面的,线程id是在用户层面的。
前面说过在Linux中没有提供专门的线程方法,使用进程模拟的线程,所以Liunx中的线程其实是轻量级进程,依旧用的task_struct,所以CPU调度知认PCB;那进程由PID,线程也该有个数字来标识这些task_struct,那这个标识就是LWP,所以LWP实际上是供CPU调度使用的(类似文件描述符一样)
线程id是用户层的,LWP是内核层的,那id怎么知道他是对应哪个task_struct的呢?所以线程的数据块一定也包含一个LWP,形成1:1的关系(也有1:n,即一个线程id包含多个LWP,在用户层面看来是一个线程)
相关文章:

Linux操作系统学习(线程基础)
文章目录线程的基础概念线程控制内核LWP和线程ID的关系线程的基础概念 一般教材对线程描述是:是在进程内部运行的一个分支(执行流),属于进程的一部分,粒度要比进程更加细和轻量化 一个进程中是可能存在多个线程…...

YOLOv5源码逐行超详细注释与解读(1)——项目目录结构解析
前言 前面简单介绍了YOLOv5的网络结构和创新点(直通车:【YOLO系列】YOLOv5超详细解读(网络详解)) 在接下来我们会进入到YOLOv5更深一步的学习,首先从源码解读开始。 因为我是纯小白,刚开始下…...

前端开发总结的一些技巧和实用方法(2)
本文主要介绍一些JS中用到的小技巧和实用方法,可以在日常Coding中提升幸福度,也可以通过一些小细节来增加代码可读性,让代码看起来更加优雅,后续将不断更新1.数组 map 的方法 (不使用Array.Map) Array.from 还可以接受第二个参数…...

Docker搭建jenkins(Vue自动化部署)
前言 需要提前准备的条件 Docker环境 一、jenkins镜像 # 查询镜像 docker search jenkins# 下载镜像 # lts稳定版 docker pull jenkins/jenkins:lts#查看镜像 docker images二、启动Jenkins容器 创建挂载文件夹,并且进行文件授予权限 #创建文件夹 mkdir -p /home/j…...

ADCS攻击之CVE-2022–26923
CSDN自动博客文章迁移漏洞简介该漏洞允许低权限用户在安装了 Active Directory 证书服务 (AD CS) 服务器角色的默认 Active Directory 环境中将权限提升到域管理员。在默认安装的ADCS里就启用了Machine模板。漏洞利用添加机器账户,并将该机器账户dnsHostName指向DC[…...

AO3401-ASEMI低压P沟道MOS管AO3401
编辑:ll AO3401-ASEMI低压P沟道MOS管AO3401 型号:AO3401 品牌:ASEMI 封装:SOT-23 最大漏源电流:-4.2A 漏源击穿电压:-30V RDS(ON)Max:0.05Ω 引脚数量࿱…...

【STM32MP157应用编程】3.控制PWM
目录 PWM文件 指令操作PWM 程序操作PWM 程序说明 程序代码 3_PWM_1.c 启动交叉编译工具 编译 拷贝到开发板 测试 PWM文件 在/sys/class/pwm目录下,存放了PWM的文件。 pwmchip0和pwmchip4目录对应了MP157 SoC的2个PWM控制器,pwmchip0对应的是M…...

基于Python的selenium
一、安装 1.1安装Python,安装Python时需要勾选增加环境变量 如果之前已经安装过Python,需要将Python相关文件以及环境变量删除 1.2安装成功:在命令行界面下输入Python,最终展示>>>即可成功 2.1安装pycharm,直接自定义安装…...

Go底层原理:一起来唠唠GMP调度(一)
目录前言一、进程、线程、Goroutine1、进程与线程2、Goroutine二、Go调度器设计思想1、线程模型1.1 内核级线程模型1.2 用户级线程模型1.3 混合型线程模型2、 被废弃的 G-M 调度器2.1 了解 G-M 调度如何工作3、如今高效的 GMP 模型3.1 GMP模型调度流程3.2 GMP调度设计策略3.3 G…...

前端——1.相关概念
这篇文章主要介绍前端入门的相关概念 1.网页 1.1什么是网页? 网站:是指在因特网上根据一定的规则,使用HTML等制作的用于展示特定内容相关的网页集合 网页:是网站中的一“页”,通常是HTML格式的文件,它要…...

java四种线程池(基本使用)
标题java四种线程池及使用示例 1、线程工厂 1、我们先来写ThreadFactory,在创建线程池时候可以传入自定义的线程工厂,线程工厂说白了就是用来定制线程的一些属性:名字、优先级、是否为守护线程。直接看代码即可。 当然创建线程池的时候可以…...

float的表示范围为什么比long大
●很多人会有一个疑问, 一个用来表示小数的 float 为什么表示的范围会比 long 还要大呢 ? ●这次, 咱们就来详细说一说这个事情 从长计议 ●聊到这个话题, 我们就要从计算机存储数字这个位置说起了 ●计算机存储数字的方式其实就是 : 二进制 二进制是计算机中最基本的数字存储…...
Flutter Android 打包保姆式全流程 2023 版
大家好,我是 17。 为什么要写这篇文章呢?对于一没有 android 开发经验,从未有过打包经历的新人来说,要想成功打包,是很困难的。因为受到的阻碍太多,是完全陌生的领域,几乎是寸步难行。如果有老…...

C++笔记之lambda表达式
引言 Lambda表达式是从C 11版本引入的特性,利用它可以很方便的定义匿名函数对象,通常作为回调函数来使用。大家会经常拿它和函数指针,函数符放在一起比较,很多场合下,它们三者都可以替换着用。 语法 [ captures ] (…...

flink大数据处理流式计算详解
flink大数据处理 文章目录flink大数据处理二、WebUI可视化界面(测试用)三、Flink部署3.1 JobManager3.2 TaskManager3.3 并行度的调整配置3.4 区分 TaskSolt和parallelism并行度配置四、Source Operator(资源算子)五、Sink Operator(输出算子)六、Flink滑…...

Java面试题(二十三)DCL单例
懒汉式单例 private static SingletonInstance INSTANCE;private SingletonInstance(){}public static SingletonInstance getInstance() {if (INSTANCE null) {INSTANCE new SingletonInstance();}return INSTANCE;}构造方法私有化,然后判断是否为空,…...

UML-类图
一、类 一个类由三个格子组成,从上至下分别表示: 第一格:类名称(接口和抽象类,使用斜体) 第二格:类的属性(成员变量,可以没有) 第三格:类的操作&…...

PostgreSQL 数据库和 pgAdmin 4
PostgreSQL 数据库和 pgAdmin 4PostgreSQLPostgreSQL 数据库安装PostgreSQL 数据库安装 (Ubuntu)PostgreSQL 数据库其他系统安装PostgreSQL 数据库快速使用入门登录数据库访问数据库参考pgAdmin 4pgAdmin 4 安装使用 pgAdmin 4 登录数据库参考PostgreSQL PostgreSQL 数据库安装…...

quarkus 搭建与基础开发环境配置总结
quarkus搭建与基础开发环境配置总结 大纲 基础概念quarkus2.13.7脚手架工程配置配置maven3.8.7quarkus快速启动quarkus的三种打包方式quarkus将程序打包为二进制文件window环境下quarkus云原生二进制文件打包环境搭建使用GraalVM-java11替换本地java8运行二进制文件 基础概念…...

扩散模型DDPM开源代码的剖析【对应公式与作者给的开源项目,diffusion model】
扩散模型DDPM开源代码的剖析【对应公式与作者给的开源项目,diffusion model】一、简介二、扩散过程:输入是x_0和时刻num_steps,输出是x_t三、逆扩散过程:输入x_t,不断采样最终输出x_0四、具体参考算法流程图五、模型mo…...

C语言 学生记录管理系统
学生记录管理系统 1--添加 2--删除 3--查询:按姓名 4--查询:按班级 5--查询:按学号 0--退出 请选择操作序号(0—5):1 请输入新学生的学号:1 请输入新学生的…...

【独家】华为OD机试 C 语言解题 - 交换字符
最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南)华为od机试,独家整理 已参加机试人员的实战技巧文章目录 最近更新的博客使用说明本期…...

网络安全平台测试赛 easyphp(phar脏数据处理)
昨天的比赛,14.00-17.00.时间有点紧张,比赛期间没拿下来这道 😭非常痛苦,很顺畅的思路 一步步想下来,卡在最后一步末尾脏数据处理了,最后时间到了 没打通,还需多练 这里本地复现一下࿱…...

【python】XML格式文件读写详解
注:最后有面试挑战,看看自己掌握了吗 文章目录XML介绍格式XML与AJAX与HTML区别联系生成XML文件案例用SAX模块处理XML用DOM模块处理XML🌸I could be bounded in a nutshell and count myself a king of infinite space. 特别鸣谢:…...

理解js的精度问题
参考博客:js精度丢失问题-看这篇文章就够了(通俗易懂)、探寻 JavaScript 精度问题以及解决方案、JavaScript 浮点数陷阱及解法 1 为什么 JavaScript 中所有数字包括整数和小数都只有一种类型 即 Number类型,它的实现遵循 IEEE 754 标准。 符号位S&#…...

蓝桥杯 时间显示
题目 输入输出样例 示例 1 输入 46800999输出 13:00:00示例 2 输入 1618708103123输出 01:08:23评测用例规模与约定 对于所有评测用例,给定的时间为不超过 10^{18}1018 的正整数。 运行限制 最大运行时间:1s最大运行内存: 512M 基础知识 时间的转换…...

qt中设置菜单高度
如题所示,我建立一个菜单,代码如下,但是菜单项的高度太小了, { popupMenu new QMenu(this); QAction *action1 new QAction(tr(“&New1”), this); QAction *action2 new QAction(tr(“&New2”), this); QA…...

测开:前端基础-css页面布局-定位
一 、传统网页布局的三种方式 网页布局的本质–用CSS来摆放盒子,把盒子摆放到相应的位置,css提供了三种传统布局方式,分别是标准流,浮动和定位三种。 二、 定位 2.1 啥是定位 我的理解,就是要把这个元素,…...

Servlet中八个监听器介绍
一、监听对象创建的监听器 1、ServletContextListener /*** 用于监听ServletContext对象创建和销毁的监听器* since v 2.3*/public interface ServletContextListener extends EventListener {/*** 对象创建时执行此方法。该方法的参数是ServletContextEvent事件对象…...

LicenseBox Crack,对服务器的要求最低
LicenseBox Crack,对服务器的要求最低 LicenseBox是用于管理基于PHP的软件、WordPress插件或主题、主题、插件和WordPress的更新和许可的完整软件。它易于安装,对服务器的要求最低,用户友好的界面,无限脚本的使用为您的创造力打开了大门。 Li…...