Linux——信号量和(环形队列消费者模型)
Linux——线程条件变量(同步)-CSDN博客
文章目录
目录
文章目录
前言
一、信号量是什么?
二、信号量
1、主要类型
2、操作
3、应用场景
三、信号量函数
1、sem_init 函数
2、sem_wait 函数
3、sem_post 函数
4、sem_destroy 函数
5、sem_getvalue 函数
6、sem_trywait 函数
7、sem_timedwait 函数
四、环形队列
1、定义与原理
2、操作
五、线程池
基本原理
主要功能
实现方式
六、基于环形队列的消费者模型
1、main函数
2、RingQueue.hpp
3、Task.hpp
编辑
前言
信号量(Semaphore)是一种用于多线程或多进程环境下实现同步和互斥的机制。
一、信号量是什么?
信号量本质上是一个计数器,用于控制对共享资源的访问。它的值表示当前可用的资源数量。当一个线程或进程想要访问某个共享资源时,它需要先检查信号量的值。如果信号量的值大于 0,则表示有可用资源,该线程或进程可以获取资源并将信号量的值减 1;如果信号量的值为 0,则表示没有可用资源,该线程或进程需要等待,直到其他线程或进程释放资源,使信号量的值大于 0。
二、信号量
1、主要类型
- 二进制信号量:也称为互斥信号量,它的值只能是 0 或 1。主要用于实现互斥访问,确保在任何时刻只有一个线程或进程能够访问共享资源,就像一个房间只有一把钥匙,谁拿到钥匙谁才能进入房间使用里面的资源,使用完后把钥匙放回,其他人才有机会拿到钥匙进入。
- 计数信号量:其值可以是任意非负整数,用于控制同时访问共享资源的线程或进程数量。比如有一个停车场有 10 个停车位,就可以用一个初始值为 10 的计数信号量来表示,每有一辆车进入停车场,信号量的值就减 1,当信号量的值为 0 时,表示停车场已满,后续车辆需要等待。
2、操作
- P 操作:也称为 wait 操作或 down 操作。当一个进程或线程执行 P 操作时,它会检查信号量的值。如果信号量的值大于 0,则将信号量的值减 1,然后进程或线程可以继续执行;如果信号量的值为 0,则进程或线程会被阻塞,放入等待队列,直到信号量的值大于 0。
- V 操作:也称为 signal 操作或 up 操作。当一个进程或线程执行 V 操作时,它会将信号量的值加 1。如果此时有其他进程或线程正在等待该信号量(即信号量的值为 0 且有进程在等待队列中),则系统会从等待队列中唤醒一个进程或线程,使其能够执行 P 操作并获取资源。
3、应用场景
- 资源管理:可以用于管理系统中的各种资源,如内存、文件、网络连接等。通过信号量可以确保资源的合理分配和使用,避免资源冲突和过度使用。
- 进程同步:在多个进程或线程协同工作的场景中,信号量可以用于实现进程之间的同步。例如,一个进程需要等待另一个进程完成某个任务后才能继续执行,就可以使用信号量来实现这种等待和唤醒机制。
- 生产者 - 消费者问题:是信号量应用的经典场景。生产者进程生产数据并将其放入缓冲区,消费者进程从缓冲区中取出数据进行消费。通过信号量可以控制生产者和消费者的行为,确保缓冲区不会被过度写入或读取。
三、信号量函数
1、sem_init 函数
- 功能:用于初始化一个信号量。
- 原型:
int sem_init(sem_t *sem, int pshared, unsigned int value);
- 参数:
sem
是指向要初始化的信号量的指针;pshared
指定信号量是否在进程间共享,0 表示仅在线程间共享,非 0 表示在进程间共享;value
是信号量的初始值。 - 返回值:成功时返回 0,失败时返回 - 1,并设置
errno
以指示错误原因。
2、sem_wait 函数
- 功能:对信号量执行 P 操作,即等待信号量变为可用。
- 原型:
int sem_wait(sem_t *sem);
- 参数:
sem
是指向要操作的信号量的指针。 - 返回值:成功时返回 0,若信号量的值为 0,则线程会阻塞直到信号量可用;失败时返回 - 1,并设置
errno
。
3、sem_post 函数
- 功能:对信号量执行 V 操作,释放信号量,使信号量的值加 1。
- 原型:
int sem_post(sem_t *sem);
- 参数:
sem
是指向要操作的信号量的指针。 - 返回值:成功时返回 0,失败时返回 - 1,并设置
errno
。
4、sem_destroy 函数
- 功能:销毁一个信号量,释放相关资源。
- 原型:
int sem_destroy(sem_t *sem);
- 参数:
sem
是指向要销毁的信号量的指针。 - 返回值:成功时返回 0,失败时返回 - 1,并设置
errno
。
5、sem_getvalue 函数
- 功能:获取信号量的当前值。
- 原型:
int sem_getvalue(sem_t *sem, int *sval);
- 参数:
sem
是指向要查询的信号量的指针;sval
是一个整数指针,用于存储信号量的当前值。 - 返回值:成功时返回 0,并将信号量的当前值存储在
sval
指向的位置;失败时返回 - 1,并设置errno
以指示错误原因。
6、sem_trywait 函数
- 功能:尝试对信号量执行 P 操作,但不会阻塞线程。如果信号量的值大于 0,则将信号量的值减 1 并立即返回;如果信号量的值为 0,则立即返回错误,而不会阻塞线程。
- 原型:
int sem_trywait(sem_t *sem);
- 参数:
sem
是指向要操作的信号量的指针。 - 返回值:成功时返回 0,此时表示成功获取信号量并将其值减 1;如果信号量的值为 0,无法获取信号量,则返回 - 1,并将
errno
设置为EAGAIN
。
7、sem_timedwait 函数
- 功能:对信号量执行 P 操作,但会设置一个超时时间。如果在超时时间内信号量变为可用,则获取信号量并返回;如果超时时间已过,信号量仍不可用,则返回错误。
- 原型:
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
- 参数:
sem
是指向要操作的信号量的指针;abs_timeout
是一个指向struct timespec
结构体的指针,用于指定绝对超时时间。 - 返回值:成功时返回 0,若在超时时间内未获取到信号量,则返回 - 1,并将
errno
设置为ETIMEDOUT
。
四、环形队列
1、定义与原理
- 环形队列是一种基于队列的数据结构,它将队列的首尾相连,形成一个环形的存储空间。与普通队列不同,环形队列可以更有效地利用存储空间,避免了普通队列在元素出队后出现的前端空闲空间无法利用的问题。
- 它通过使用两个指针,即队头指针(front)和队尾指针(rear)来管理队列中的元素。当队尾指针到达队列的末尾时,它会重新回到队列的开头,继续存储新元素,从而实现了循环利用空间的功能。
2、操作
- 初始化:创建一个指定大小的数组来存储队列元素,并将队头指针和队尾指针都初始化为 0,表示队列为空。
- 入队操作:当要将一个新元素插入到环形队列中时,首先检查队列是否已满。如果未满,将新元素存储在队尾指针所指向的位置,然后将队尾指针向后移动一位。如果队尾指针已经到达数组的末尾,则将其重新设置为数组的开头位置。
- 出队操作:从环形队列中删除元素时,首先检查队列是否为空。如果不为空,取出队头指针所指向的元素,然后将队头指针向后移动一位。同样,如果队头指针到达数组的末尾,也需要将其重新设置为数组的开头位置。
- 判断队列空满
- 一般采用牺牲一个存储空间的方法来区分队列空和满的情况,即当
(rear + 1) % maxSize == front
时,认为队列已满,其中maxSize
是队列的最大容量;当front == rear
时,认为队列是空的。 - 也可以使用一个计数器来记录队列中元素的个数,当计数器的值为 0 时表示队列为空,当计数器的值等于
maxSize
时表示队列已满。
- 一般采用牺牲一个存储空间的方法来区分队列空和满的情况,即当
五、线程池
线程池是一种多线程处理形式,它将多个线程预先创建并放入一个池中,以方便对线程进行管理和重复利用,从而提高系统性能和资源利用率。以下是关于线程池的详细介绍:
基本原理
- 线程创建与管理:线程池在初始化时会创建一定数量的线程,并将它们放入线程池中。这些线程在创建后不会立即执行具体任务,而是处于等待状态,等待接收任务并执行。
- 任务队列:线程池通常会维护一个任务队列,用于存储待执行的任务。当有新任务到来时,会将任务添加到任务队列中。线程池中的线程会不断从任务队列中获取任务,并执行相应的操作。
- 线程复用:线程执行完一个任务后,不会立即销毁,而是返回到线程池中,继续等待下一个任务。这样可以避免频繁地创建和销毁线程,减少了线程创建和销毁所带来的开销,提高了系统的性能和响应速度。
主要功能
- 提高资源利用率:通过复用线程,避免了因频繁创建和销毁线程而带来的资源浪费,尤其是在处理大量短时间任务时,能显著提高系统资源的利用率。
- 控制并发度:可以限制同时执行的线程数量,避免过多线程同时运行导致系统资源过度消耗,从而保证系统的稳定性和响应能力。
- 简化线程管理:将线程的创建、调度和管理等工作封装在一个线程池中,使得开发者无需直接管理大量的线程,降低了多线程编程的复杂性,提高了代码的可维护性和可读性。
实现方式
- 线程池的组成部分
- 线程集合:存储线程池中的所有线程,一般使用线程数组或线程列表来实现。
- 任务队列:用于存放待执行的任务,通常使用队列数据结构,如阻塞队列来实现。当任务队列满时,新任务可能会被阻塞或根据特定的策略进行处理。
- 线程池管理模块:负责线程池的初始化、线程的创建与销毁、任务的分配与调度等管理工作。它根据任务队列的状态和线程池的配置参数,决定是否需要创建新的线程或回收空闲线程。
- 工作流程
- 任务提交:用户将任务提交到线程池,任务会被放入任务队列中。
- 任务分配:线程池中的线程会不断从任务队列中获取任务。当线程获取到任务后,就开始执行任务。
- 线程管理:线程池管理模块会监控线程的状态,当线程执行完任务后,会将其重新放回线程池中,使其可以继续执行下一个任务。如果线程池中的线程数量超过了最大线程数,或者有空闲线程超过了一定的空闲时间,线程池管理模块会负责销毁这些线程,以释放资源。
六、基于环形队列的消费者模型
1、main函数
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <ctime>
#include "RingQueue.hpp"
#include "Task.hpp"using namespace std;struct ThreadData
{RingQueue<Task> *rq;std::string threadname;
};void *Productor(void *args)
{// sleep(3);ThreadData *td = static_cast<ThreadData*>(args);RingQueue<Task> *rq = td->rq;std::string name = td->threadname;int len = opers.size();while (true){// 1. 获取数据int data1 = rand() % 10 + 1;usleep(10);int data2 = rand() % 10;char op = opers[rand() % len];Task t(data1, data2, op);// 2. 生产数据rq->Push(t);cout << "Productor task done, task is : " << t.GetTask() << " who: " << name << endl;sleep(1);}return nullptr;
}void *Consumer(void *args)
{ThreadData *td = static_cast<ThreadData*>(args);RingQueue<Task> *rq = td->rq;std::string name = td->threadname;while (true){// 1. 消费数据Task t;rq->Pop(&t);// 2. 处理数据t();cout << "Consumer get task, task is : " << t.GetTask() << " who: " << name << " result: " << t.GetResult() << endl;// sleep(1);}return nullptr;
}int main()
{srand(time(nullptr) ^ getpid());RingQueue<Task> *rq = new RingQueue<Task>(50);pthread_t c[5], p[3];for (int i = 0; i < 1; i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Productor-" + std::to_string(i);pthread_create(p + i, nullptr, Productor, td);}for (int i = 0; i < 1; i++){ThreadData *td = new ThreadData();td->rq = rq;td->threadname = "Consumer-" + std::to_string(i);pthread_create(c + i, nullptr, Consumer, td);}for (int i = 0; i < 1; i++){pthread_join(p[i], nullptr);}for (int i = 0; i < 1; i++){pthread_join(c[i], nullptr);}return 0;
}
2、RingQueue.hpp
#pragma once
#include <iostream>
#include <vector>
#include <semaphore.h>
#include <pthread.h>const static int defaultcap = 5;template<class T>
class RingQueue{
private:void P(sem_t &sem){sem_wait(&sem);}void V(sem_t &sem){sem_post(&sem);}void Lock(pthread_mutex_t &mutex){pthread_mutex_lock(&mutex);}void Unlock(pthread_mutex_t &mutex){pthread_mutex_unlock(&mutex);}
public:RingQueue(int cap = defaultcap):ringqueue_(cap), cap_(cap), c_step_(0), p_step_(0){sem_init(&cdata_sem_, 0, 0);sem_init(&pspace_sem_, 0, cap);pthread_mutex_init(&c_mutex_, nullptr);pthread_mutex_init(&p_mutex_, nullptr);}void Push(const T &in) // 生产{P(pspace_sem_);Lock(p_mutex_); // ?ringqueue_[p_step_] = in;// 位置后移,维持环形特性p_step_++;p_step_ %= cap_;Unlock(p_mutex_); V(cdata_sem_);}void Pop(T *out) // 消费{P(cdata_sem_);Lock(c_mutex_); // ?*out = ringqueue_[c_step_];// 位置后移,维持环形特性c_step_++;c_step_ %= cap_;Unlock(c_mutex_); V(pspace_sem_);}~RingQueue(){sem_destroy(&cdata_sem_);sem_destroy(&pspace_sem_);pthread_mutex_destroy(&c_mutex_);pthread_mutex_destroy(&p_mutex_);}
private:std::vector<T> ringqueue_;int cap_;int c_step_; // 消费者下标int p_step_; // 生产者下标sem_t cdata_sem_; // 消费者关注的数据资源sem_t pspace_sem_; // 生产者关注的空间资源pthread_mutex_t c_mutex_;pthread_mutex_t p_mutex_;
};
3、Task.hpp
#pragma once
#include <iostream>
#include <string>std::string opers="+-*/%";enum{DivZero=1,ModZero,Unknown
};class Task
{
public:Task(){}Task(int x, int y, char op) : data1_(x), data2_(y), oper_(op), result_(0), exitcode_(0){}void run(){switch (oper_){case '+':result_ = data1_ + data2_;break;case '-':result_ = data1_ - data2_;break;case '*':result_ = data1_ * data2_;break;case '/':{if(data2_ == 0) exitcode_ = DivZero;else result_ = data1_ / data2_;}break;case '%':{if(data2_ == 0) exitcode_ = ModZero;else result_ = data1_ % data2_;} break;default:exitcode_ = Unknown;break;}}void operator ()(){run();}std::string GetResult(){std::string r = std::to_string(data1_);r += oper_;r += std::to_string(data2_);r += "=";r += std::to_string(result_);r += "[code: ";r += std::to_string(exitcode_);r += "]";return r;}std::string GetTask(){std::string r = std::to_string(data1_);r += oper_;r += std::to_string(data2_);r += "=?";return r;}~Task(){}private:int data1_;int data2_;char oper_;int result_;int exitcode_;
};
相关文章:

Linux——信号量和(环形队列消费者模型)
Linux——线程条件变量(同步)-CSDN博客 文章目录 目录 文章目录 前言 一、信号量是什么? 二、信号量 1、主要类型 2、操作 3、应用场景 三、信号量函数 1、sem_init 函数 2、sem_wait 函数 3、sem_post 函数 4、sem_destroy 函数 …...
【JOIN】关键字在MySql中的详细使用
目录 INNER JOIN(内连接) LEFT JOIN(左连接) RIGHT JOIN(右连接) FULL JOIN(全连接) 示例图形化解释JOIN的不同类型 INNER JOIN: LEFT JOIN: RIGHT J…...

渗透测试--攻击常见的Web应用
本文章咱主要讨论,常见Web应用的攻击手法,其中并不完全,因为Web应用是在太多无法囊括全部,但其中的手法思想却值得我们借鉴,所以俺在此做了记录,希望对大家有帮助!主要有以下内容: 1…...

window系统annaconda中同时安装paddle和pytorch环境
一、下载nvidia驱动 Download The Official NVIDIA Drivers | NVIDIA 查看GPU信息 nvidia-smi 二、安装cuda CUDA Toolkit 11.8 Downloads | NVIDIA Developer 按以下步骤下载cuda安装包,我使用的cuda11.8 下载后双击一路下一步安装即可。 查看cuda版本 nvcc …...

python-leetcode-简化路径
71. 简化路径 - 力扣(LeetCode) class Solution:def simplifyPath(self, path: str) -> str:# 使用栈来处理路径stack []# 分割路径,以 / 为分隔符parts path.split(/)for part in parts:if part or part .:# 空字符串或 .࿰…...
浅谈 PID 控制算法
PID 控制算法概念 在我们的生活中可能大家都没有听说过 PID 控制算法,但它可以说是无处不在,小到空调的温度控制、无人机的精准悬停、机器人运作系统,大到飞机和火箭的飞行姿态控制都有 PID 的身影。 PID 控制算法,即比例 - 积分…...

ailx10的专栏电子书(2022版)
最近整理了一下自己的知乎专栏,基于myBase和html help workshop做了一本电子书,一共20个章节,接近280M,19块9,有兴趣的同学私信我,记录了从我上学到工作这些年来的心得体会,以及学习历程&#x…...

WPS按双字段拆分工作表到独立工作簿-Excel易用宝
我们老板真是事多,他说要把这个工作表以月份和支付方式的维度,以这两个字段进行拆分工作表,而且拆分出来的表格要保存一个新的工作簿。 啥事都交给我,他还以为我有三头六臂呢,还好我有易用宝,可以轻松拆分…...

C++ Qt练习项目 日期时间数据 未完待续
个人学习笔记 新建项目 设计UI 实现组件功能 参考资料 4.7日期时间数据_哔哩哔哩_bilibili...

vim文本编辑器
vim命令的使用: [rootxxx ~]# touch aa.txt #首先创建一个文件 [rootxxx ~]# vim aa.txt #vim进入文件aa.txt进行编辑 vim是vi的升级版,具有以下三种基本模式: 输入模式(编辑模式) 点击i进入编辑模式 (说明…...
产品经理面试题总结2025【其一】
一、产品理解与定位 1、你如何理解产品经理这个角色? 作为一名互联网产品经理,我理解这个角色的核心在于成为产品愿景的制定者和执行的推动者。具体来说,产品经理是连接市场、用户和技术团队之间的桥梁,负责理解市场需求、用户痛…...

资料03:【TODOS案例】微信小程序开发bilibili
样式 抽象数据类型 页面数据绑定 事件传参...

玉米植物结构受乙烯生物合成基因 ZmACS7 的调控
摘要: 植物高度和叶片角度是玉米(Zea mays)植物结构的两个关键决定因素,与高种植密度下的抗倒伏性和冠层光合作用密切相关。这两个性状主要由几种植物激素调节。然而,乙烯在调节玉米植物结构中的机制,特别…...
C#语言的函数实现
C#语言的函数实现详解 C#是一种功能强大的编程语言,以其易于学习和强大的功能而备受欢迎。在C#中,函数(或称为方法)是构建程序的基本单位,它们可以封装特定的功能和逻辑。本文将详细讲解C#语言中函数的概念、定义、调…...
1.6 从 GPT-1 到 GPT-3.5:一路的风云变幻
从 GPT-1 到 GPT-3.5:一路的风云变幻 人工智能的进步一直是科技领域的一个重要话题,而在自然语言处理(NLP)领域,GPT(Generative Pre-trained Transformer)系列模型的发布,标志着一个又一个技术突破。从2018年发布的 GPT-1 到2022年推出的 GPT-3.5,OpenAI 的每一次更新…...
TypeScript - 利用GPT辅助学习
TypeScript 一、基础1. 安装 TypeScript2. 创建你的第一个 TypeScript 文件3. 编译 TypeScript 代码4. 变量声明与类型注解5. 函数与类型注解6. 总结 二、进阶常用类型1. 类型别名2. 对象类型3. 类型断言4.typeof 操作符 高级类型1. 类2. 交叉类型3. 泛型与 keyof4. 索引签名类…...
VMware虚拟机迁移到阿里云
VMware虚拟机迁移到阿里云是一个涉及多个步骤的过程,具体如下: 使用阿里云的服务器迁移中心(SMC)进行P2V或V2V迁移。如果是小型应用,可以通过制作镜像文件然后上传至阿里云OSS,并基于该镜像创建ECS实例。对…...

【STM32-学习笔记-15-】MAX7219点阵屏模块
文章目录 MAX7219点阵模块一、MAX7219Ⅰ、 概述Ⅱ、功能特点Ⅲ、引脚功能Ⅳ、典型应用电路Ⅴ、内部组成结构Ⅵ、时序图Ⅶ、寄存器 二、STM32控制点阵屏Ⅰ、程序框图Ⅱ、程序编写①、MAX7219.c②、MAX7219.h③、MAX7219_Img.h④、main.c MAX7219点阵模块 一、MAX7219 Ⅰ、 概述…...

高并发内存池_CentralCache(中心缓存)和PageCache(页缓存)申请内存的设计
三、CentralCache(中心缓存)_内存设计 (一)Span的创建 // 页编号类型,32位下是4byte类型,64位下是8byte类型 // #ifdef _WIN64 typedef unsigned long long PageID; #else _WIN32 typedef size_t PageI…...

elementUI Table组件实现表头吸顶效果
需求描述 当 table 内容过多的时候,页面上滑滚动,表头的信息也会随着被遮挡,无法将表头信息和表格内容对应起来,需要进行表头吸顶 开始编码💪 环境:vue2.6、element UI step1: 给el-table__h…...
RTOS学习之重难点
📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...
[c#]判定当前软件是否用管理员权限打开
有时一些软件的逻辑中需要使用管理员权限对某些文件进行修改时,那么该软件在执行或者打开的场合,就需要用使用管理员身份运行才能达到效果。那么在c#里,如何判定该软件是否是对管理员身份运的呢? 1.取得当前的windows用户。 2.取得…...

机器学习:集成学习概念和分类、随机森林、Adaboost、GBDT
本文目录: 一、集成学习概念**核心思想:** 二、集成学习分类(一)Bagging集成(二)Boosting集成(三)两种集成方法对比 三、随机森林(一)构造过程(二…...
https相比http的区别
https相比http的区别 https相比http的区别在于:https使用了SSL/TLS加密协议,确保数据传输的安全性和完整性,通信时需要证书验证。 https相比于http的区别主要在于安全性。https使用SSL/TLS加密传输数据,确保数据在客户端和服务器之间的通信…...

uniapp 安卓 APP 后台持续运行(保活)的尝试办法
在移动应用开发领域,安卓系统的后台管理机制较为复杂,应用在后台容易被系统回收,导致无法持续运行。对于使用 Uniapp 开发的安卓 APP 来说,实现后台持续运行(保活)是很多开发者面临的重要需求,比…...
PyTorch中matmul函数使用详解和示例代码
torch.matmul 是 PyTorch 中用于执行矩阵乘法的函数,它根据输入张量的维度自动选择适当的矩阵乘法方式,包括: 向量内积(1D 1D)矩阵乘向量(2D 1D)向量乘矩阵(1D 2D)矩…...
《视觉SLAM十四讲》自用笔记 第二讲:SLAM系统概述
在rm队伍里作为算法组梯队队员度过了一个赛季,为了促进和负责其他工作的算法组成员的交流,我决定在接下来的半个学期里(可能更快)读完这本书,并将其中的部分理论应用于我自制的雷达导航小车上。 以下为第二讲的部分笔记…...
基于 COM 的 XML 解析技术(MSXML) 的总结
✅ 一、COM 与 MSXML 简要说明 🔷 什么是 COM? COM(Component Object Model)是一种 Windows 平台下的组件技术,可以实现在不重新编译代码的前提下复用组件。 特点: 用 接口调用方式 解耦依赖;…...

PDF 转 HTML5 —— HTML5 填充图形不支持 Even-Odd 奇偶规则?(第一部分)
在填充 PDF 中的图形时(以及许多其他技术中),你可以选择使用 Even-Odd(奇偶) 或 Non-Zero(非零) 填充规则。 对于那些已经在想“你在说啥?”的朋友,别担心,我…...
计算机视觉与深度学习 | 基于MATLAB的图像特征提取与匹配算法总结
基于MATLAB的图像特征提取与匹配算法全面指南 图像特征提取与匹配 基于MATLAB的图像特征提取与匹配算法全面指南一、图像特征提取基础特征类型分类二、点特征提取算法1. Harris角点检测2. SIFT (尺度不变特征变换)3. SURF (加速鲁棒特征)4. FAST角点检测5. ORB (Oriented FAST …...