- 博客(92)
- 收藏
- 关注
原创 【Linux】 --- 线程池
我们将线程池进行了模板化,因此线程池当中存储的任务类型可以是任意的,但无论该任务是什么类型的,在该任务类当中都必须包含一个Run方法,当我们处理该类型的任务时只需调用该Run方法即可。此时线程池内的线程不断从任务队列拿出任务进行处理,而它们并不需要关心这些任务是哪来的,它们只需要拿到任务后执行对应的Run方法即可。主线程就负责不断向任务队列当中Push任务就行了,此后线程池当中的线程会从任务队列当中获取到这些任务并进行处理。线程池是一种线程使用模式。主函数:main.cc。
2025-03-29 15:39:23
1115
原创 【Linux】 --- 信号量
也就是说,当环形队列为空和满时,我们已经通过信号量保证了生产者和消费者的串行化过程。当执行流在申请信号量时,可能此时信号量的值为0,也就是说信号量描述的临界资源已经全部被申请了,此时该执行流就应该在该信号量的等待队列当中进行等待,直到有信号量被释放时再被唤醒。每个执行流在进入临界区之前都应该先申请信号量,申请成功,就有了操作特定的临界资源的权限,当操作完毕后就应该释放信号量。多个执行流为了访问临界资源会竞争式的申请信号量,因此信号量是会被多个执行流同时访问的,也就是说信号量本质也是临界资源。
2025-03-29 15:36:41
653
原创 【Linux】--- 基于阻塞队列:生产消费者模型
我们也可以当阻塞队列当中存储的数据大于队列容量的一半时,再唤醒消费者线程进行消费;当阻塞队列当中存储的数据小于队列容器的一半时,再唤醒生产者线程进行生产。
2025-03-28 09:40:15
976
原创 【Linux】--- 线程互斥
既然–操作需要三个步骤才能完成,那么就有可能当thread1刚把tickets的值读进CPU就被切走了,也就是从CPU上剥离下来,假设此时thread1读取到的值就是1000,而当thread1被切走时,寄存器中的1000叫做thread1的上下文信息,因此需要被保存起来,之后thread1就被挂起了。临界区内的线程完全可能进行线程切换,但即便该线程被切走,其他线程也无法进入临界区进行资源访问,因为此时该线程是拿着锁被切走的,锁没有被释放也就意味着其他线程无法申请到锁,也就无法进入临界区进行资源访问了。
2025-03-24 20:44:13
730
原创 【Linux】--- 线程概念、线程控制
线程的标准概念:线程是进程中的的一个执行流,是CPU调度的基本单位!进程:进程是系统资源分配的基本单位!一切进程至少都有一个执行线程。线程在进程内部运行,本质是在进程地址空间内运行。关于进程需要明确的是,一个进程的创建实际上伴随着其进程控制块(task_struct)、进程地址空间(mm_struct)以及页表的创建,虚拟地址和物理地址就是通过页表建立映射的。每个进程都有自己独立的进程地址空间和独立的页表,也就意味着所有进程在运行时本身就具有独立性。关于线程。
2025-03-08 22:31:55
1064
原创 【Linux】--- 信号阻塞、信号捕捉
此处我们用(2) SIGINT做检测,先通过sigaddset(&set, 2)把set中的第二位变为1,随后通过sigprocmask(SIG_BLOCK, &set, nullptr)将set添加到block中,由于我们并不想知道旧的block是什么样,所以第三个参数设为nullptr。,指向信号的处理函数。如果时机合适,进程会检测pending表和block表,然后检测出已经接收到的信号,若该信号未被阻塞,执行对应信号的处理函数,并把pending中的该位变回0,表示该信号已经处理完了。
2025-02-17 20:33:06
877
原创 【Linux】--- 信号的概念、信号产生
再比如说我们之前的ctrl + C按键发送(2) SIGINT信号,本质也是硬件中断,当我们从键盘输入了数据后,键盘向CPU发出硬件中断,随后CPU去执行操作系统中的硬件中断程序,发现是用户按下了ctrl + C,于是操作系统向进程发送(2) SIGINT信号。以上就是Linux中的全部信号,它们分为两个区间:[1, 31] 和[34, 64],也就是说没有0,32,33这三个信号,虽然信号的最大编号为64,但实际上只有62个信号。经理收到这个信号后,会暂停当前的工作,转而去处理员工的请求。
2025-02-15 21:52:36
1110
原创 【Linux】--- 进程间的通信
进程间通信简称IPC(Interprocess communication),进程间通信就是在不同进程之间传播或交换信息。管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的数据流称为一个“管道”。例如,统计我们当前使用云服务器上的登录用户个数。其中,who命令和wc命令都是两个程序,当它们运行起来后就变成了两个进程,who进程通过标准输出将数据打到“管道”当中,wc进程再通过标准输入从“管道”当中读取数据,至此便完成了数据的传输,进而完成数据的进一步加工处理。
2025-02-11 22:07:02
747
原创 【Linux】--- 基础IO
该函数会返回一个FILE*的指针,C语言中,通过操作这个 FILE * 来控制文件的IO。当我们打开文件时,每个被使⽤的⽂件都在内存中开辟了⼀个相应的⽂件信息区,⽤来存放⽂件的相关信息。这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE。我们可以通过操纵这个FILE类型的结构体,来操控文件。大部分情况下,我们可以得到一个指向该结构体的指针FILE*,即文件指针,后续通过文件指针来操控文件。上图中,三个指针pf1,pf2,pf3,它们都指向了一个FILE类型的文件信息区。
2025-02-03 20:45:32
1385
原创 【Linux】--- 进程的等待与替换
例如,以下代码中同时创建了10个子进程,同时将子进程的pid放入到ids数组当中,并将这10个子进程退出时的退出码设置为该子进程pid在数组ids中的下标,之后父进程再使用waitpid函数指定等待这10个子进程。第一个参数是要执行程序的路径,第二个参数是一个指针数组,数组当中的内容表示你要如何执行这个程序,数组以NULL结尾,第三个参数是你自己设置的环境变量。第一个参数是要执行程序的路径,第二个参数是可变参数列表,表示你要如何执行这个程序,并以NULL结尾,第三个参数是你自己设置的环境变量。
2025-01-15 21:31:08
1216
原创 【Linux】--- Linux中进程的创建与终止
退出码都有对应的字符串含义,帮助用户确认执行失败的原因,而这些退出码具体代表什么含义是人为规定的,不同环境下相同的退出码的字符串含义可能不同。我们都知道main函数是代码的入口,但实际上main函数只是用户级别代码的入口,main函数也是被其他函数调用的,是间接性被操作系统所调用的。当子进程刚刚被创建时,子进程和父进程的数据和代码是共享的,即父子进程的代码和数据通过页表映射到物理内存的同一块空间。新进程为子进程,而原进程为父进程。3、为什么父进程返回的是子进程的pid,子进程返回的是0?
2025-01-15 11:58:11
941
原创 【Linux】--- 进程的概念
操作系统描述进程的时候就是PCB,PCB(process control block)在Linux中的PCB叫做:task_struct(task_struct就是PCB的一种)创建进程不仅仅是,把代码和数据加载到内存里,还要为进程创建task_struct所以:进程 = task_struct + 代码和数据根据:“先描述,再组织”①描述:task_struct在Linux内核中就是一种:结构体(里面包含着进程的相关信息)
2024-12-16 22:10:55
1303
原创 【Linux】--- 开发工具篇:yum、vim、gcc、g++、gdb、make、makefile
gdb是GNU开源组织发布的一个强大的UNIX下的程序调试工具,是命令行调试工具。一般来说,gdb主要完成如下四个功能:(1)启动程序,按照自定义要求随心所欲运行程序。(2)可让被调试的程序在指定的调试的断点处停住。(断点可以是条件表达式)(3)当程序被停住时,可以检查此时程序中所发生的事。(4)动态的改变程序的执行环境gcc 源文件 -o 目标文件 -g。
2024-10-30 21:39:34
1399
原创 【优选算法】--- 分治 快速排序
第一步:对数组排序,划分区间,【l,left】【left+1,right-1】【right,r】根据题目要求,我们需要把数组中的元素012按顺序排好。把 “ 1 ”当做key基准元素。采用:快排+三指针的解法。
2024-10-08 15:03:15
1226
原创 【优选算法】---分治 归并排序
(4)最后再一左一右利用双指针的算法,固定一个然后移动另外一个在:一左一右->这两个数组里面分别找逆序对的个数,最后加起来就是整个数组的逆序对个数。这里的重点和难点就在于:我们如何找到我们正在遍历的数组(是打乱排完序的)中当前元素的原始下标!(3)然后再在右半部分找逆序对的个数再排排序,(2)分别在左半部分找逆序对的个数,排完序。分治的思想:归并排序类似于二叉树的后序遍历。1、运用递归分别:处理左半部分、右半部分。这样的解法的时间复杂度:N*logN。但是这次我们要排的是:降序!但是有一点需要特别注意,
2024-10-07 22:55:28
886
原创 【C++】---STL之用哈希桶模拟实现:unordered_set和unordered_map
将__HTIterator中的_node更改为HashTable指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节,在64位平台上是8字节),这样可以通过编译。如果我们要用哈希桶对unordered_set和unordered_map进行封装的话,我们面临一个问题那就是 unordered_set传给哈希桶的是K,而unordered_map传给哈希桶的是pair,同样我们之前也遇到过set和map的封装,是用的红黑树。迭代器最重要的部分呢~
2024-09-03 16:00:29
1242
原创 【C++】---哈希算法
简单的说,就是运用->哈希思想,而形成的数据结构!就是找出各个数据对应的储存位置的转换公式!按照这种方法查找不用拿key多次比较,不用像链表2叉树等等那些数据结构进行多次比较才会查找到结果。因此查找的速度比较快。哈希冲突:就是说,不同的数据,通过哈希函数,来计算出它所对应的映射位置的时候,位置相同,就是哈希冲突!比如说这里的10001和11,%10之后,他们所得的值都是1,都应该放在下标为1的位置,这就是哈希冲突!引起哈希冲突的原因:哈希函数设计不合理。
2024-08-27 11:09:59
1683
原创 【C++】--- set和map的封装
把红黑树节点定义 由类模板// 这里的 T就是上层set/map第2个形参 传来的类型T _data;由于红黑树不知道上层传的是K还是pair,这是由上层传递的模板参数T决定的,上层是封装我的map和set。private:红黑树的迭代器的本质是对节点指针进行封装,所以迭代器中只有封装红黑树节点指针这一个成员变量。//成员变量。
2024-08-19 18:08:36
1006
原创 【C++】---AVL树详解
由于要实现AVL树的增删改查,所以定义AVL树的节点,就需要定义parent,否则插入节点时,不知道要链接到树里面哪个节点下面。// 结点int _bf;// 平衡因子// 构造函数,_kv(kv),_bf(0)public://构造函数AVLTree(){}return;
2024-08-10 19:01:23
1047
原创 【C++】--- STL之 set 和 map
multimap容器与map容器的底层实现一样,也都是平衡搜索树(红黑树),其次,multimap容器和map容器所提供的成员函数的接口都是基本一致的,这里也就不再列举了,multimap容器和map容器的区别与multiset容器和set容器的区别一样,multimap允许键值冗余,即。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。是最常用的,也是最简洁的!
2024-05-30 23:03:59
1167
21
原创 【C++】---二叉搜索树
二叉搜索树又叫二叉排序数,它或者是空树,或者是具有以下性质的二叉树:比如说:这个数组都可以将它化为二叉搜索树总结:在左子树值比根小,右子树值比根大。 当树走中序遍历时,序列都是有序的。二叉搜索树 的 结构定义:二、二叉搜索树操作(非递归)1.二叉搜索树的查找 (非递归)利用二分查找的方法,借助我们去二叉搜索树中查找节点。查找的时间复杂度:最坏的情况,就是查找高度(h=logN)次,就可以判断一个值在不在节点里面。查找的思路:(2)中序遍历由于根节点_root是私有成员变量,如果在main函数
2024-05-29 22:42:02
1853
39
原创 【C++】---多态
1. 子类必须对父类的虚函数进行重写(重写,包括三同:返回值相同,函数名相同,参数列表相同。协变除外)2. 必须是父类的指针或者引用去调用虚函数,而且被调用的函数必须是虚函数。注意:如果不把析构函数都定义为虚函数的话,可能会发生内存泄漏!1、自己好好想为什么建议把析构函数定义虚函数,防止发生内存泄漏?因为如果所有的析构函数都定义为虚函数之后,它们所有的析构函数就会形成多态的关系,形成多态之后,就会达到我们的期望:delete的时候,子类对象调用子类的析构函数,父类对象要用父类的析构函数。
2024-05-22 22:58:10
1082
39
原创 【C++】---继承
继承机制是面向对象程序设计当中使代码复用的重要手段,它允许程序员,在保持原有类特性的基础上,进行拓展,增加功能,产生新的类,这个新的类就叫做:派生类。继承的出现,呈现出面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们所接触到的复用都是函数的复用,而继承的出现则是体现了:类设计层次的复用。// 基类public:protected:// 派生类:stuprotected:int stu_ID;// 派生类:teaprotected:int job_ID;
2024-05-08 22:04:56
1411
16
原创 【C++】---模板进阶
【优点】1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生2. 增强了代码的灵活性【缺陷】1. 模板会导致代码膨胀问题,也会导致编译时间变长3. 出现模板编译错误时,错误信息非常凌乱,不易定位错误好了,今天的分享就到这里了我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
2024-05-01 15:54:09
1129
10
原创 【LeetCode】---150. 逆波兰表达式求值
(3)代码给定的入参是vector不是string,因为不知道一个操作数有几个项,得用特殊符号比如空格去分割,而示例每项都是一个字符串,vector的类型是string就更方便获取表达式的每一个元素。(2)但是中缀转后缀表达式和中缀表达式不同的就是调整运算符的优先级。
2024-05-01 15:53:34
168
原创 【LeetCode】---15.最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。void push(int val) 将元素val推入堆栈。int getMin() 获取堆栈中的最小元素。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。MinStack() 初始化堆栈对象。
2024-04-30 21:19:22
240
原创 【C++】---STL容器适配器之priority_queue
(1)priority_queen是一种:优先级队列,是一种常见的容器适配器,默认情况下把最大的元素放在第1位。(2)它的底层是用堆实现的,默认情况下是大堆。可以随时插入元素,快速查找到队列中最大的元素。(3)优先级队列从特定容器的尾部弹出,称为优先级队列的顶部。(4) priority_queue的底层容器可以是任何标准容器的类模板,也可以是其他特定设计的容器类。empty( ):检测容器是否为空size( ):返回容器中有效元素个数front( ):返回容器中第一个元素的引用。
2024-04-28 22:17:50
1584
22
原创 【C++】---STL容器适配器之底层deque浅析
stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可 以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有 push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,不适合大量的头部和中间插入删除,也不适合大量的随机访问。1、什么是deque?
2024-04-28 22:17:02
1200
7
原创 【C++】---STL容器适配器之queue
(3)底层容器可以是标准容器类模板之一,如可用vector、list可以作为底层容器类,也可以是其他专门设计的容器类,。(1)队列是一种容器适配器,容器适配器说白了,它的底层就是用一个标准的容器来封装实现的。专门用于在FIFO:先进先出,从容器的一端插入,一端删除。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque。(2)队列作为容器适配器实现,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。关注我,让我们一起学习,一起成长吧!
2024-04-27 20:53:51
640
27
原创 【C++】---STL容器适配器之stack
(1)适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个 类的接口 转换成客户希望的另外一个接口。(2)举个例子:比如对于笔记本来说,电源额定电压是220V,而美国电压是110V,为了能在美国使用,必须要用变压器转换电压以匹配美国电压,那么这个变压器就是个适配器。为什么stack和queue不是容器?而叫做容器适配器???(3)虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,
2024-04-27 19:58:43
1199
9
原创 【C++】---STL之list的模拟实现
的重载的时候,我们一定要想清楚到底是对它里面节点的值来判断相不相等,还是说来判断指向这个结点的迭代器指针相不相等。但是链表不一样,物理空间连续所以说我要把这个迭代器进行一个类的封装,然后在里面对他运算符重载(例如:++)我们就可以掌控这个迭代器的行为!对于T&,类模板实例化出两个类,一个是T&类,一个是const T&类,同理,T*也一样。下面这里就是构造了一个迭代器,因为它的返回类型是迭代器,你只要有节点的指针我就可以构造一个迭代器,(2)迭代器有两种,一种是普通迭代器,一种是const的迭代器。
2024-04-25 22:16:32
1095
13
原创 【C++】---STL之list详解
erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代 器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是 没有元素的,那么pos就失效了。因此删除list中任意位置上元素时,vs就认为该位置迭代器失效 了。我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!2、优点:可以在任意位置进行插入删除,插入删除的效率比较高。好了,今天的分享就到这里了。
2024-04-24 15:47:49
1137
29
原创 【C++】---STL之vector的模拟实现
当我们不写vector的拷贝构造函数,编译器会自动生成一份,但编译器生成的只能完成数据的浅拷贝,然而vector的三个成员变量都是迭代器,也就是指针T*,如果T是内置类型,那么就不会出现问题,2、然后再判断是否需要扩容,如果进行了扩容,那么就需要先保存要插入的pos位置到start之间的相对位置。2、然后再要删除位置的下一个位置定义一个指针,然后从这个指针到结尾的位置的数据依次往前挪动覆盖数据。(2)当resize的大小比原来大,说明空间不够,同时也说明容量可能不够,要判断是否需要申请容量。
2024-04-23 21:38:01
815
10
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人