Linux之线程概念,理解和控制
Linux之线程概念,理解和控制
- 一.线程
- 1.1线程的概念
- 1.2线程的理解
- 1.3线程的优缺点,异常和用途
- 1.4线程和进程
- 1.5线程的控制
- 1.4.1线程的创建
- 1.4.2线程的终止
- 1.4.3线程的等待
- 1.4.4线程的分离
一.线程
1.1线程的概念
在我们了解了进程的同时我们在学校上课时可能还听说过线程的概念,那么进程和线程又有什么区别他们俩又有什么联系呢?
在我们的书本中对于线程的概念是:线程是比进程更加轻量级的一种执行流或者是线程是在进程内部执行的执行流。
但是书本中的概念有点不好理解如果直白一点好理解一点的话线程和进程的概念是:线程是CPU调度的基本单位,进程是分配系统资源的基本单位。现在让我们带着这些概念来学习线程到底是什么它和进程之间又有什么联系呢?
这是我们在学习了进程之后我们所了解的整个运行逻辑,如果让我们来设计一个线程呢?大家又会怎么设计呢?
我们知道进程=属性+内容,而进程的属性是存储在pcb中的并且我们在调度进程时也是通过将进程的pcb移到各个队列中来进行调度的。在我们所了解的进程中我们只知道进程有一个pcb,那么如果我们假设一个进程的代码中有五个函数我们是否可以创建五个pcb来分别对应其中的一个函数呢?这些pcb分别指向不同的函数而且在cpu进行调度时也是分别调度这五个pcb。所以这五个pcb就相当于五个执行流而这些存在于进程中的执行流就是线程。
1.2线程的理解
那么在大概了解了线程后我们来想一下我们要如何看待以前的进程呢?
很简单,我们把之前的进程当作只有一个执行流的进程即可也叫做单线程进程,而我们今天学习的这种在进程内部有多个线程的进程就是多线程进程。
我们再回过头来对照一下书本中对于线程的概念以及我们对于线程和进程概念的总结看是否和我们图中花的线程是相符合的。
书中说线程是一种比进程更加轻量级的一种执行流,在之前的描述中我们知道cpu对于进程的调度就是调度它的pcb那么调度pcb是不是就是调度进程中的线程呢?所以线程肯定是比进程更加轻量级的。书中还说线程是在进程内部执行的一种执行流,同样从图中我们可以看出来线程肯定是处于进程内部的因为线程参与了进程资源的分配所以它在执行的时候使用的就是进程内部的资源。同时我们要注意一件事情:线程是参与了进程资源的分配的其中这个资源不止是指进程的代码数据,还包括了进程的时间片。我们知道当CPU调度一个进程时是有调度的时间的一旦到了这个时间后CPU就会切换进程而这个时间就是时间片。所以线程同样会参与对时间片的分配导致如果是一个多线程进程那么每个线程的调度时间是在原来的时间片的基础上均分的。
我们对于线程的概念的总结是线程是CPU调度的基本单位,我们知道CPU调度进程是调度其中的pcb那么如今pcb就等于是线程所以CPU调度的就是线程。对于进程的概念的总结是进程是参与系统资源分配的基本单位,当我们从磁盘中每次想要执行一个可执行文件时就要创建出一个进程来执行它,所以会产生进程的pcb,虚拟地址空间,页表等结构。
在知道了进程的存在后我们就要思考一个问题:我们可以创建多个线程是吧,那么如果我们真的支持这样的线程的话线程是否要被管理呢?要如何管理呢?
对线程的管理是很简单的我们只需要遵守先描述再组织的规则就可以将对线程的管理变成对数据结构的增删查改,但是我们知道如果你使用这种方法来管理线程我们就需要为线程设计一整套的管理方案其中包括线程的属性,内容以及对于线程的调度算法。这又是一个不小的工作量而且我们发现线程的属性很多都是可以继承进程的属性的毕竟线程是存在于进程内部的。那么属性可以继承进程的调度方法是不是也可以继承进程的呢?但是这样的话它还是线程吗?
这件牵涉到了不同操作系统对于线程的不同处理方法,在我们日常使用的Windows系统下真的为线程设计了一整套的方法,而对于Linux系统来说它不想为了个线程浪费太多的功夫所以它将线程改了个名字叫做轻量化进程,这个轻量化进程就继承了进程的部分属性和调度方法。如果要给这两种方法分个优劣的话肯定是Linux的方法更好更加的优雅。
在知道了在Linux中线程其实就是轻量化进程后我们就有疑问了,这个轻量化到底是轻量在什么地方呢?
线程创建的更加简单了。
以前我们想要创建一个进程时我们需要pcb,虚拟地址空间和页表但是现在我们创建一个线程只需要再创建出一个pcb来分配进程的资源即可。
线程切换的效率更高。
- 我们之前使用单线程进程时CPU想要切换进程就必须将进程在CPU内部的寄存器内容全部保存起来,但是如今使用多线程进程时我们想要切换进程就只需要保存少量的寄存器内容即可,因为线程是处于进程内部的导致线程会共享一部分的进程数据所以那些寄存器内容是不用保存的。
- 不仅是需要切换的寄存器数量少了线程切换还不需要重新更新缓存即cache。在了解其中的原理之前我们先解释一个计算机界的一个现象:局部性原理。
局部性原理是计算机科学中的一个重要概念,它描述了一个现象:在一段时间内,程序倾向于仅使用一部分代码或数据。这种倾向性分为两类:时间局部性和空间局部性。时间局部性指的是如果某个数据项被访问,那么它不久后可能会被再次访问。空间局部性则是指如果访问了某个存储单元,那么其附近的存储单元也很可能不久后会被访问。
因为有局部性原理这个现象所以CPU中存在了缓存即cache这个部件,在我们使用CPU执行代码时其实不是一条一条的来读取代码的而是将要执行的代码以及其附近的代码一并保存在cache中,而这些被存储在cache中的数据就被叫做热数据。由于局部性原理当其中的一条代码被执行后附近的代码也很大的可能性被执行这样CPU就只需要在cache中读取下一条代码后执行即可从而来提升读取执行的效率。
而在我们进行进程切换的时候因为每个进程的代码不同所以我们需要更新cache中的数据,这个操作也被叫做热更新。但是当切换线程时因为都是处于一个进程之下所以代码是相同的就不需要更新cache中的内容从而加快效率。
在使用线程之前我们还需要更新一次对于虚拟地址空间的认识,从认识虚拟地址空间到我们知道是CPU使用页表和mmu来进行虚拟到物理的地址转换再到我们今天要重新认识虚拟地址空间,我们已经三顾虚拟地址空间了。
对于以往的虚拟地址空间我们可以用一张图来概括
所以在我们的理解里页表中的每一行即页表项是包含了虚拟地址和物理地址的映射以及物理内存是否有数据是否被使用的标识符的。假设我们将虚拟地址和物理地址分别视作4字节的数据,后面的标识符视作2字节的数据,那么一个页表项加起来是10字节的数据。
对于虚拟地址空间从0000 0000到FFFF FFFF一共是2的32次方个字节也就是4GB的内容,如果我们要让每个虚拟地址都对物理内存进行映射那么需要有2的32次方个页表项而每个页表项又是10字节所以一个页表的大小就达到了40GB,这可能吗?这不可能,所以我们对于页表的构成是完全错误的!!
那么真实的页表又是什么样的呢?这就要先聊到我们之前学习文件系统时说到的文件块了,我们知道在文件系统中最小的存储单元是4kb也就是一个文件块,所以对于在磁盘中可执行文件我们是否也可以将其视作大量的文件块组成的一个文件呢?那么我们是否也可以将物理内存视作以文件块为最小存储单位从而组成的一个内存呢?
同时我们也能发现系统是如何对内存进行管理的了,如今的内存不就是由4GB/4KB即1,048,576个页框组成的吗,那么遵守先描述再组织的原理我们只需要对每个页框创建一个结构体对象其中存储了这个页框是否使用以及页框的属性即可,之后将每个页框的结构体对象存储到一个数组中,这样数组的下标就是对于着是哪个页框同时对于对内存的管理也就转换为了对数组内容的增删查改。
那么在内存的最小存储单元都是4KB的时候我们要如何通过虚拟地址来找到对应的物理内存呢?也就要说到我们重新认识的页表了。
其实在操作系统内部是将虚拟地址分为了三部分来进行虚拟到物理的转换的,一个地址是4个字节也就是32个bit位所以系统将其分为了10,10,12的三部分。其中的第一部分是由页目录进行管理的,页目录是由2的10次方即1,024个地址组成的一个数组其中数组的下标是代表了数据的前十个比特位而数组中每个成员的内容则是指向一个页表。而这个页表也是一个由2的10次方即1024个地址组成的一个数组,页表的下标则是数据的中间十位的bit位,其每个成员内容则是对应的页框的起始地址。而最后12个比特位也就是2的12次方即4,096个地址就是页框内的偏移量。光说可能不太好理解其中的原理我们可以通过图来演示。
所以通过前20个比特位来查找到页框的起始地址,再通过最后的12个比特位来确定页内的偏移量从而找到内存中的每一个数据。
那么通过比特位来划分页表不就是来划分地址空间吗。所以在进程的眼中虚拟地址空间也是资源的一种,所以线程参与进程资源的分配也就是包括了对应虚拟地址空间的划分也就是对页表的划分。
1.3线程的优缺点,异常和用途
线程的优点:
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多
- 能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作
线程的缺点:
- 性能损失
当我们使用过多的线程来执行代码时会导致同步和调度的开销过大从而造成效率的损失 - 健壮性下降
对于多线程代码我们需要细致入微的来编写因为一旦有一个线程出现错误就会造成整个进程的崩溃。 - 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。 - 编程难度提高
编写与调试一个多线程程序比单线程程序困难得多
线程的异常:
当一个线程出现异常时会导致其他的线程也发生崩溃,并且会让整个进程也崩溃。这是因为当线程异常时收到的信号是对进程产生作用一旦异常就会让整个进程退出那么其他的线程也就退出了。也就是为什么多线程进程的健壮性会下降的原因。
为了证明这个现象同时也提前让大家熟悉一下线程的代码,我来用代码给大家演示一下。
#Makefile
pthread:pthread.ccg++ -o $@ $^ -lpthread.PHONY:clean
clean:rm -f pthread
//phread.cc
#include <iostream>
#include <pthread.h>
#include <unistd.h>//线程的异常
void* ThreadRoutine(void* args)
{//新线程int cnt = 5;while(cnt--){std::cout << "我是新线程" << std::endl;sleep(1);}int i = 10;i /= 0;
}
int main()
{pthread_t tid;pthread_create(&tid,NULL,ThreadRoutine,NULL);//主线程while(true){std::cout << "我是主线程" << std::endl;sleep(1);}return 0;
}
在学习了信号后我们知道Floating point exception就是因为发出了8号信号而产生的
如果我们想要查看是否产生了新线程的话我们可以使用ps -aL来查看
线程的用途:
- 合理的使用多线程,能提高CPU密集型程序的执行效率
- 合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是多线程运行的一种表现)
1.4线程和进程
线程和进程的区别
- 进程是资源分配的基本单位
- 线程是调度的基本单位
- 线程共享进程数据,但也拥有自己的一部分数据:
- 线程ID
- 一组寄存器(重要)
- 栈(重要)
- errno
- 信号屏蔽字
- 调度优先级
- 各线程还共享以下进程资源和环境:
- 文件描述表
- 每种信号的处理方法
- 当前工作目录
- 用户id和组id
那么如何证明线程是由自己的栈的呢?
我们知道Linux为了支持线程所以搞了一个pthread原生线程库,这个库里封装的都是系统调用接口从而来让用户把其当作线程的控制接口。而这个pthread_create封装的就是clone这个系统调用接口。
所以线程和的关系我们可以用一张图来概括
1.5线程的控制
通过上面的代码我们大概知道如何使用线程,但是大家是否仔细观察了Makefile中我在g++编译器后加了一个选项-lpthread。这是不是看着很眼熟,这不就是我们之前学习动态库时通过编译器来链接动态库的方法吗,可是为什么我们创建线程需要链接一个动态库呢?难道线程的函数不是由我们系统内部实现的?
这就要说到Linux中实现线程的方法了,之前我们说过Linux中是通过轻量化进程来实现线程的功能的所以在Linux中其实是不存在线程这个概念的!
那么问题就来了如果我们是一个从Windows下转来Linux的一个程序员,在Windows中我们使用的就是线程结果来了Linux后你告诉我Linux里没有线程只有轻量化进程那我不是还得去学习轻量化进程的概念还有使用方法吗?这也太麻烦了吧。所以Linux为了避免这种麻烦它在系统内部实现了一个叫做pthread原生线程库的一个动态库,只要使用编译器链接上这个动态库你就可以直接和其他系统一样创建线程最多就是创建线程的函数方法不同而已。
想要证明这个结论我们在后续学习线程的控制接口的时候会再次提到。
1.4.1线程的创建
通过线程的创建我们发现创建线程是会返回线程的id的那么这个id是否和我们之前用ps -aL查询的LWP是一个呢?
大家想想我是怎么介绍LWP的,LWP是轻量化进程的ID不是线程的ID,在Linux中没有线程的概念只有轻量化进程。所以我们通过ps -aL查到的都是轻量化进程的id不是线程的id,那么这个线程的id到底是什么呢?
我们知道原生线程库是一个动态库所以它是存放在虚拟地址空间中的共享区的,而我们想要搞清楚这个id到底是什么就必须要知道pthread_create的第一个参数到底是什么意思,我们知道第一个参数是一个输出性参数所以我们只需要定义一个pthread_t变量再传入到函数中即可,那么函数到底做了什么让他变成了线程的id呢?
其实当我们创建一个线程时会在原生线程库中让一段空间存储线程的属性,线程的局部存储以及线程的栈地址。而函数会将这段空间的首地址赋予到第一个参数上,所以我们说的那个线程的id其实就是虚拟地址空间中原生线程库的一个地址。
那么LWP和这个id的区别就很明显了,LWP是内核级别的轻量化进程的id,而这个线程id则是进程调度所使用的一个id,因为我们在进行线程切换的时候肯定需要让一个唯一的数值来指向一个线程从而方便线程的切换。
1.4.2线程的终止
如果想要一个线程终止则有三种方法
- 线程自己return
这个方法就很简单了,自己return了不就终止了 - 线程调用pthread_exit函数
参数是当调用这个函数退出后的返回值,需要配合线程的等待来使用,也可以设为NULL。 - 一个线程使用pthread_cancel函数来终止另外一个线程
// 线程的终止
static int number = 0;
void *ThreadRoutine(void *args)
{std::string ThreadName = (char *)args;// 新线程while (true){sleep(1);std::cout << "i am a new thread, my name is:" << ThreadName << number << std::endl;}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"Thread-");// 主线程int cnt = 5;while (true){if (cnt-- == 0){// 主线程让新线程终止// 一般最好让主线程最后终止// 所以建议不要让新线程调用pthread_cancle函数让主线程终止pthread_cancel(tid);}std::cout << "i am a main thread" << std::endl;sleep(1);}return 0;
}
1.4.3线程的等待
为什么要进行线程的等待呢?其实原因和为什么要进行进程的等待是相似的,都是为了防止僵尸状态的产生从而发生数据泄露的问题,但是线程我们没办法用代码让大家看到线程的“僵尸状态”。
而线程等待的函数就是pthread_join
// 线程的等待
static int number = 0;
void *ThreadRoutine(void *args)
{std::string ThreadName = (char *)args;// 新线程// 1.通过return终止// int cnt = 3;// while(cnt--)// {// std::cout << "i am a new thread" << std::endl;// sleep(1);// }// return (void*)"new thread quit";// 2.通过pthread_exit终止// int cnt = 3;// while (cnt--)// {// std::cout << "i am a new thread" << std::endl;// sleep(1);// }// pthread_exit((void *)"new thread done");// 3.通过pthread_cancel终止while (true){std::cout << "i am a new thread" << std::endl;sleep(1);}
}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"Thread-");// 主线程// 1.通过return终止// void *c;// int n = pthread_join(tid, &c);// std::cout << "i am a main thread\n"// << "n:" << n << std::endl;// std::cout << (char*)c << std::endl;// return 0;// 2.通过pthread_exit终止// void *c;// pthread_join(tid, &c);// std::cout << "i am a main thread\n";// std::cout << (char*)c << std::endl;// return 0;// 3.通过pthread_cancel终止sleep(5);pthread_cancel(tid);void *c;int n = pthread_join(tid, &c);std::cout << "i am a main thread\n"<< "n:" << n << std::endl;std::cout << (int64_t)c << std::endl;return 0;
}
1.4.4线程的分离
在学习了线程的等待后我们就必须要等待其他的线程了吗?有没有什么方法可以让新线程不需要被等待呢?
有,那就是将新线程分离出去,默认情况下我们创建的线程都是joinable的也就是需要进行等待的,但是如果我们使用pthread_detach将新线程进行分离那么就等于是告诉系统这个线程我不需要它的返回值,你直接在它退出的时候给它资源释放了吧。
//线程的等待
static int number = 0;
void *ThreadRoutine(void *args)
{std::string ThreadName = (char *)args;// 新线程//我们可以使用pthread_self函数来获取当前线程的id//从而实现自己分离pthread_detach(pthread_self());int cnt = 5;while (cnt--){sleep(1);std::cout << "i am a new thread, my name is:" << ThreadName << number << std::endl;}}int main()
{pthread_t tid;pthread_create(&tid, nullptr, ThreadRoutine, (void *)"Thread-");// 主线程while (true){std::cout << "i am a main thread" << std::endl;sleep(1);}return 0;
}
相关文章:
Linux之线程概念,理解和控制
Linux之线程概念,理解和控制 一.线程1.1线程的概念1.2线程的理解1.3线程的优缺点,异常和用途1.4线程和进程1.5线程的控制1.4.1线程的创建1.4.2线程的终止1.4.3线程的等待1.4.4线程的分离 一.线程 1.1线程的概念 在我们了解了进程的同时我们在学校上课时…...
系统监控——分布式链路追踪系统
摘要 本文深入探讨了分布式链路追踪系统的必要性与实施细节。随着软件架构的复杂化,传统的日志分析方法已不足以应对问题定位的需求。文章首先解释了链路追踪的基本概念,如Trace和Span,并讨论了其基本原理。接着,文章介绍了SkyWa…...
【Leetcode Top 100】146. LRU 缓存
问题背景 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 c a p a c i t y capacity capacity 初始化 LRU 缓存int get(int key) 如果关键字 k e y key key 存在于缓存中&…...
Ubuntu Server 22.04.5 LTS重启后IP被重置问题
Ubuntu Server 22.04.5 LTS重启后IP被重置问题 最近在使用Ubuntu Server 22.04做项目开发测试时发现每次重启和关机后,所设置的静态IP地址都会回复到安装系统时所设置的ip Ubuntu Server 22.04 官网下载地址:Ubuntu官方下载地址 对虚拟机下安装Ubuntu感…...
电机功率、电压与电流的换算方法
在电气工程和相关行业中,电机的功率、电压和电流是三个重要的基本参数。它们之间有着密切的关系,而理解这些关系对于电机的选型、设计和应用至关重要。本文将详细阐述这三者之间的换算关系,以及相关公式的应用。 一、电机功率的定义 电机功…...
【Java】反射简介
框架的核心和架构师的核心 反射和代理是重中之重 反射 反射的作用 在运行的时候由代码获取类的信息 三种获取类信息的方式: 对象.getClass()Class.forName("类的路径")类.class Class :一个用来存储类信息的类 获取类信息是获取的整体的…...
【JAVA】Java第十三节:String类(String相关方法,以及StrinBuftrer , StringBulder相关方法)
本文详细介绍了String类以及常用的String相关方法,以及StrinBuftrer , StringBulder相关方法的使用,建议有印象即可,不需要都记住,使用时去查取即可 一、创建一个String类型的变量 我们平时创建String类型的变量一般是第一种形式…...
电子信息工程自动化 基于单片机的出租车计价器设计
摘 要 出租车作为一种城市中非常重要的公共交通工具,他与人们的生活息息相关。所以我也设计了一款出租车计价器,它采用模块化设计,包含里程测量模块、数据存储模块、按键模块、时钟模块、显示模块、语音播报模块六大主要模块。本设计的出租车…...
CentOS 二进制安装部署MongoDB 4.0
一、安装MongoDB 1. 下载 MongoDB 二进制文件 前往 MongoDB 官方下载页面(https://www.mongodb.com/try/download/community) 选择对应版本的 tar 包。 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.28.tgz 2. 解压并移动至目标目录 解压文件ÿ…...
SQL面试题——京东SQL面试题 合并数据
京东 合并数据 几天的题目来自知名电商平台京东 已知有数据A如下,请分别根据A生成B和C。 数据A +-----+-------+ | id | name | +-----+-------+ | 1 | aa | | 2 | aa | | 3 | aa | | 4 | d | | 5 | c | | 6 | aa | | 7 | aa | | …...
windows安装使用conda
在Windows系统上安装和使用Conda的详细步骤如下: 一、下载Conda安装包 访问Conda的官方网站Anaconda | The Operating System for AI,点击“Downloads”按钮。在下载页面,选择适合您系统的安装包。通常,对于Windows系统…...
C++知识整理day4内存管理——new和delete详解
文章目录 1.C/C内存分布2.C语言中动态内存管理:malloc/realloc/calloc3.C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型 4.malloc/free和new/delete到底什么区别?4.1 对于自定义类型4.2 对于自定义类型4.3 总结:它们…...
STM32 自学笔记
摘抄于大学期间记录在QQ空间的一篇自学笔记,当前清理空间,本来想直接删除掉的,但是感觉有些舍不得,因此先搬移过来。 RAM vs ROM vs FLASH 2013-09-05记录,ROM和RAM指的都是半导体存储器,ROM是Read Only …...
spring通过RequestContextHolder获取HttpServletRequest对象
1.获取HttpServletRequest对象方法: public static HttpServletRequest getRequest() {ServletRequestAttributes attributes ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());assert attributes ! null;return attributes.getRequest(…...
【特殊子序列 DP】力扣1137. 第 N 个泰波那契数
泰波那契序列 Tn 定义如下: T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n,请返回第 n 个泰波那契数 Tn 的值。 示例 1: 输入:n 4 输出:4 解释: T_3 0 1 1 2 T_4 1 …...
tcp连接设置一个超时时间(没在操作系统层面设置)
await asyncio.open_connection(ip, port, limit1024)代码是使用了操作系统的TCP连接,正常TCP连接的时候会有重试机制,当第一个SYN没有回复的时候,会再重试4次,每次间隔1s, 2s,4s, 8s,我觉得太慢了…...
03、Node.js安装及环境配置
1.下载node.js 下载地址:Node.js 2.安装 2.1 自定义安装路径(可以选择默认) 下图根据本身的需要进行,我选择了默认Node.js runtime,然后Next: Node.js runtime :表示运行环境 npm package mana…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Remote Communication Kit
1.问题描述: DynamicDnsRule有没有示例?这个地址是怎么解析出来 https://developer.huawei.com/consumer/cn/doc/harmonyos-references/remote-communication-rcp-0000001770911890#section8160554134811 解决方案: ‘DynamicDnsRule’&a…...
WebStorm快捷键保持跟Idea一致
修改连续行局部多选 在WebStorm中同时按下ctrl alt s; 选择KeyMap 输入Column Selection Mode选择快捷键, 右键选择Add Mouse Shortcut 按下alt 鼠标左键 如果出现占用的情况,直接删除其他使用该快捷键的地方即可; 修改跨行局部多选 在…...
14、鸿蒙学习——管理通知角标
针对未读的通知,系统提供了角标设置接口,将未读通知个数显示在桌面图标的右上角角标上。 通知增加时,角标上显示的未读通知个数需要增加。 通知被查看后,角标上显示的未读通知个数需要减少,没有未读通知时࿰…...
【词向量表示】Word2Vec原理及实现
文章目录 Word2VecHow achieveLookup tableCodingPre-dataingModelNegative sameple Word2Vec 单词与单词之间的向量往往不在同一个向量空间,例如,传统的编码方式:one-hot编码,不同单词[1, 0, 0]和[0, 1, 0]之间的余弦相似度为0。…...
【C++】位图
Ⅰ、bitset的介绍 位图: 就是用 比特位 来标识某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。 位图的接口: 成员函数 功能 set 设置指定位或所有位 reset 清空指定位或所有位 flip …...
性能测试需求分析(超详细总结)
🍅 点击文末小卡片 ,免费获取软件测试全套资料,资料在手,涨薪更快 1、客户方提出 客户方能提出明确的性能需求,说明对方很重视性能测试,这样的企业一般是金融、电信、银行、医疗器械等;他们…...
React开发 - 技术总结系列二
HOC 初体验 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。 简单点说,就是组件作为参数,返回值也是组件的函数&#x…...
Spring事务实现原理
我们一般将Spring事务使用在数据库操作上面,用来保证数据的一致性和完整性 实现原理: 通过AOP和事务管理器实现的 1.AOP拦截: 拦截Transactional注解的方法调用 2.事务管理器: 负责事务的开启,提交和回滚 3.事务…...
云服务器部署upload-labs-docker(文件上传靶场)环境 以及相关报错问题
环境的搭建 准备:云服务器(本地的linux服务器(版本最好不要是老的不然不兼容docker)) f8x配置docker环境: https://github.com/ffffffff0x/f8x 一键配置 docker拉取file-labs靶场 https://github.com…...
Python进阶编程总结
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
【第 1 章 初识 C 语言】1.8 使用 C 语言的 7 个步骤
目录 1.8 使用 C 语言的 7 个步骤 1.8.1 第 1 步:定义程序的目标 1.8.2 第 2 步:设计程序 1.8.3 第 3 步:编写代码 1.8.4 第 4 步:编译 1.8.5 第 5 步:运行程序 1.8.6 第 6 步:测试和调试程序 1.8.…...
vue3 实现音频转文字组件
使用recorder-core第三方插件实现音频转纯文本的功能。 工具类文件 recoder.ts import Recorder from recorder-core import recorder-core/src/engine/wav import recorder-core/src/extensions/lib.fft.js import recorder-core/src/extensions/frequency.histogram.view i…...
MySQL各种锁详解
什么是锁? 1.1 锁的解释 计算机协调多个进程或线程并发访问某一资源的机制。 1.2 锁的重要性 在数据库中,除传统计算资源(CPU、RAM、I/O等)的争抢,数据也是一种供多用户共享的资源。 如何保证数据并发访问的一致性&…...
前端工程 Node 版本如何选择
1. Node 与 Npm 版本对应 这是一个必知必会的问题,尤其是对于维护那些老掉牙、一坨坨、非常大的有着长期历史的老破大工程。 1.1. package-lock.json 版本 首先你要会看项目的 package-lock.json 文件中的 lockfileVersion 版本号,这对于 NPM 安装来说…...
新增白名单赋予应用安装权限
目录 相关问题 具体实现 相关问题 安装app到/data/分区时,如何在安装阶段就赋予权限,无需请求权限 具体实现 frameworks/base/core/res/res/values/config.xml <!-- For whitelis apk --><string-array translatable"false" nam…...
学习Python的笔记14--迭代器和生成器
1.迭代器(Iterator) 概念: 迭代意味着重复多次,就像循环一样。 迭代器是一个可以记住遍历的位置的对象。 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。 迭代器只能往前不会后退。 1.iter…...
【Golang】Golang基础语法之面向对象:结构体和方法
面向对象——结构 Go 仅支持封装,不支持继承和多态;继承和多态要做的事情交给接口来完成,即——面向接口编程。Go 只有 struct,没有 class。 定义一个最简单的树节点(treeNode)结构,方法如下&…...
重磅升级:OpenAI o1模型上手实测,从芯片架构分析到象棋残局判断的全能表现
引言 昨日,在圣诞节系列发布会的第一天,OpenAI终于给我们带来了令人振奋的更新,这些更新有望塑造AI互动的未来。备受期待的OpenAI o1正式版的推出,标志着ChatGPT体验的重大进化,宣告了AI驱动应用新时代的开始。o1现已可…...
Pandas处理和分析嵌套JSON数据:从字符串到结构化DataFrame
在数据分析领域,我们经常遇到需要从非结构化数据中提取有用信息的场景。特别是当数据以JSON字符串的形式出现时,如何有效地将其转换为结构化的表格形式,以便进行进一步的分析和处理,成为了一个常见的挑战。本文将通过一个具体的例…...
《ODIN: A Single Model for 2D and 3D Segmentation》CVPR2024
斯坦福和微软: 代码链接:ODIN: A Single Model For 2D and 3D Perception 论文链接:2401.02416 摘要 这篇论文介绍了ODIN(Omni-Dimensional INstance segmentation),一个能够同时处理2D RGB图像和3D点云…...
第40节 在ArkTS中实现socket功能
1. 基本概念 在 ArkTS 中实现 Socket 功能主要涉及到网络通信中的套接字(Socket)编程。Socket 是一种用于在不同设备(如客户端和服务器)之间进行双向通信的接口,它允许应用程序发送和接收数据。在网络编程中…...
Ruby On Rails 笔记1——Rails 入门
突然想跟着官方文档把Ruby On Rails过一遍,把一些有用的记下来就可以一直看了,do它! https://guides.rubyonrails.org/v7.2/ 注:官网是英文文档,我自己翻译了一下,不确保完全准确,只供自己学习开发使用。 …...
npm, yarn, pnpm之间的区别
前言 在现代化的开发中,一个人可能同时开发多个项目,安装的项目越来越多,所随之安装的依赖包也越来越臃肿,而且有时候所安装的速度也很慢,甚至会安装失败。 因此我们就需要去了解一下,我们的包管理器&#…...
Uniapp的App环境下使用Map获取缩放比例
概述 目前我试过的就是你用vue后缀是拿不到比例的你可以用nvue当然uniapp的uvue应该是更加可以的我使用的是高德所以你得在高德的后台声请原生的Android的key才可以如果是vue3的开发模式的话不用使用this来获取当前对象使用scale对象来接受和改变缩放比例会比较友好然后直接走…...
[免费]基于Python的Django在线(生鲜)商城(电子商城)管理系统【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的基于Python的Django在线(生鲜)商城(电子商城)管理系统,分享下哈。 项目视频演示 【免费】基于Python的Django在线(生鲜)商城(电子商城)管理系统 Python毕业设计_哔哩哔哩_bilibili 项目介绍 随…...
Go 1.19.4 HTTP编程-Day 20
1. HTTP协议 1.1 基本介绍 HTTP协议又称超文本传输协议,属于应用层协议,在传输层使用TCP协议。HTTP协议属是无状态的,对事务处理没有记忆能力,如果需要保存状态需要引用其他技术,如Cookie。HTTP协议属是无连接的&…...
基于微软云第一个大模型程序Java和python实现
1 注册一个微软云账号 按照提示一步一步注册,注册过程中,注册微软云账号需要visa卡。可以在某宝花钱30元买下。 2 部署模型 搜索openAI 创建资源组 部署一个模型 这个后面代码会使用 3 Java 实现 pom 依赖 <dependency><groupId>com…...
【5G】5G目标和标准化 5G targets and standardization
5G标准是在第三代合作伙伴关系项目(3GPP,3rd Generation Partnership Project)中定义的,实际的标准制定工作由参与3GPP活动的区域标准机构成员共同推进。目前,超过600家公司通过各自的地区标准组织成为3GPP的成员。然而…...
KernelShark在ubuntu24.04.01的编译
KernelShark在ubuntu24.04.01的编译 写在前面具体过程装ubuntu24.04.01安装depends下载代码如何编译cmake 输出make 输出 如何安装 初步启动Add the User to the perf Group 简单的使用trace-cmd抓包 来看我的文章,必有所得。 平凡中,总有我帮您踩过的坑…...
【Flutter】WillPopScope组件-监听物理返回键事件自定义返回事件
WillPopScope(onWillPop: () async {if ( flutterWebViewPlugin ! null && await flutterWebViewPlugin.canGoBack() true) {flutterWebViewPlugin!.goBack();return false; // 阻止默认的返回行为} else {return true; // 允许默认的返回行为}},child: Scaffold(),);…...
深度学习每周学习总结J8(Inception V1 算法实战与解析 - 猴痘识别)
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 | 接辅导、项目定制 目录 0. 总结Inception V1 简介1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数:定义损失函数&am…...
Django模板系统
1.常用语法 Django模板中只需要记两种特殊符号: {{ }}和 {% %} {{ }}表示变量,在模板渲染的时候替换成值,{% %}表示逻辑相关的操作。 2.变量 {{ 变量名 }} 变量名由字母数字和下划线组成。 点(.)在模板语言中有…...
【JavaWeb后端学习笔记】MySQL的数据操作语言(Data Manipulation Language,DML)
MySQL DML 0、准备表结构1、添加数据1.1 指定字段添加数据(单条)1.2 全部字段添加数据(单条)1.3 指定字段添加数据(批量)1.4 全部字段添加数据(批量) 2、修改数据3、删除数据 MySQL的…...