第25章 汇编语言--- 信号量与互斥锁
信号量(Semaphore)和互斥锁(Mutex,全称Mutual Exclusion Object)是两种用于管理对共享资源的访问的同步机制。它们在多线程或多进程编程中非常重要,可以确保同一时间只有一个线程或进程能够访问特定的资源,从而避免了竞争条件(Race Condition)。下面我将详细叙述这两种机制,并给出简单的伪代码示例。
信号量(Semaphore)
信号量是一种更通用的同步原语,它允许限制同时访问临界区(Critical Section)的线程数量。信号量有一个内部计数器,表示可用资源的数量。当一个线程想要访问受保护的资源时,它必须先从信号量获取许可。如果信号量的计数器大于零,则线程可以继续执行并减少计数器;如果计数器为零,则线程会被阻塞直到计数器再次大于零。
二值信号量
当信号量的计数器只能取0或1时,我们称之为二值信号量,其行为与互斥锁类似,用于保证同一时刻只有一个线程能进入临界区。
计数信号量
计数信号量可以有多个单位,允许多个线程同时访问资源,只要不超过设定的最大数量。
信号量的基本操作:
P操作(wait/sem_wait)
:尝试获取信号量,若成功则计数器减一,否则等待。V操作(signal/sem_post)
:释放信号量,增加计数器。
伪代码示例:
// 初始化信号量
sem_init(&sem, 0, 1); // 第三个参数是初始计数值// 进入临界区前
sem_wait(&sem);// 临界区代码...// 离开临界区后
sem_post(&sem);
互斥锁(Mutex)
互斥锁是一个更为严格的同步机制,它确保一次只有一个线程可以持有锁,即进入临界区。其他试图获取该锁的线程将会被阻塞,直到当前线程释放了锁。
互斥锁的基本操作:
lock操作(pthread_mutex_lock)
:尝试获取锁,若成功则持有锁,否则等待。unlock操作(pthread_mutex_unlock)
:释放持有的锁。
伪代码示例:
// 初始化互斥锁
pthread_mutex_init(&mutex, NULL);// 进入临界区前
pthread_mutex_lock(&mutex);// 临界区代码...// 离开临界区后
pthread_mutex_unlock(&mutex);
汇编语言中的实现
汇编语言中并没有直接提供信号量或互斥锁这样的高级抽象。这些同步原语通常是用操作系统提供的系统调用来实现的,而这些系统调用最终会映射到内核模式下的某些指令序列,例如x86架构上的XCHG
指令,它可以原子地交换两个值,常用于实现无锁算法和底层同步机制。
如果你希望看到具体的汇编代码来实现这些功能,这取决于具体的操作系统和硬件平台。不同的操作系统有不同的系统调用接口,而且汇编代码也会根据CPU架构的不同而有所变化。通常来说,这类底层实现是由操作系统开发者完成的,普通应用开发者一般不需要直接处理这些细节。
下面我会给出一些更具体的案例来说明如何使用信号量和互斥锁。为了简化示例,我将使用伪代码,并假设你是在一个支持POSIX线程(pthreads)的环境中工作。
案例 1:使用互斥锁保护共享资源
假设有多个线程需要访问一个共享的计数器变量。为了避免竞争条件,我们需要确保一次只有一个线程可以修改这个变量。我们可以使用互斥锁来实现这一点。
#include <pthread.h>
#include <stdio.h>// 共享资源
int counter = 0;
// 互斥锁
pthread_mutex_t mutex;void* increment_counter(void* arg) {// 尝试获取锁pthread_mutex_lock(&mutex);// 修改共享资源int local = counter;local++;counter = local;printf("Counter incremented to: %d\n", counter);// 释放锁pthread_mutex_unlock(&mutex);return NULL;
}int main() {// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建线程pthread_t threads[10];for (int i = 0; i < 10; ++i) {pthread_create(&threads[i], NULL, increment_counter, NULL);}// 等待所有线程完成for (int i = 0; i < 10; ++i) {pthread_join(threads[i], NULL);}// 销毁互斥锁pthread_mutex_destroy(&mutex);return 0;
}
案例 2:使用信号量控制并发访问
假设我们有一个生产者-消费者问题,其中多个生产者线程往缓冲区中添加数据,而多个消费者线程从缓冲区中移除数据。我们可以使用两个信号量来协调生产者和消费者之间的操作:一个用于跟踪缓冲区中的空位数量,另一个用于跟踪已填充的槽位数量。
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 5// 缓冲区
int buffer[BUFFER_SIZE];
int in = 0, out = 0;// 信号量
sem_t empty;
sem_t full;
pthread_mutex_t mutex;void* producer(void* arg) {int item;while (1) {item = rand() % 100; // 生产一项// 等待有空位sem_wait(&empty);// 进入临界区前pthread_mutex_lock(&mutex);// 添加到缓冲区buffer[in] = item;in = (in + 1) % BUFFER_SIZE;printf("Produced: %d\n", item);// 离开临界区后pthread_mutex_unlock(&mutex);// 增加full信号量sem_post(&full);// 模拟延迟sleep(rand() % 3);}
}void* consumer(void* arg) {int item;while (1) {// 等待有商品sem_wait(&full);// 进入临界区前pthread_mutex_lock(&mutex);// 从缓冲区移除item = buffer[out];out = (out + 1) % BUFFER_SIZE;printf("Consumed: %d\n", item);// 离开临界区后pthread_mutex_unlock(&mutex);// 增加empty信号量sem_post(&empty);// 模拟延迟sleep(rand() % 3);}
}int main() {// 初始化信号量和互斥锁sem_init(&empty, 0, BUFFER_SIZE); // 初始时所有位置为空sem_init(&full, 0, 0); // 初始时没有满的位置pthread_mutex_init(&mutex, NULL);// 创建生产者和消费者线程pthread_t producers[3], consumers[2];for (int i = 0; i < 3; ++i) {pthread_create(&producers[i], NULL, producer, NULL);}for (int i = 0; i < 2; ++i) {pthread_create(&consumers[i], NULL, consumer, NULL);}// 主线程等待子线程结束(这里实际应该有个合理的退出机制)for (int i = 0; i < 3; ++i) {pthread_join(producers[i], NULL);}for (int i = 0; i < 2; ++i) {pthread_join(consumers[i], NULL);}// 销毁信号量和互斥锁sem_destroy(&empty);sem_destroy(&full);pthread_mutex_destroy(&mutex);return 0;
}
请注意,上述代码片段是简化的例子,实际应用中你需要考虑更多细节,例如错误处理、线程的优雅退出等。此外,sleep()
函数用于模拟延迟,在实际应用中应当根据实际情况选择合适的同步方式。
接下来我将继续追加一些更深入的案例和概念,包括条件变量(Condition Variable)的使用、无锁编程(Lock-free Programming)的概念以及如何在实际编程中选择合适的同步机制。
案例 3:使用条件变量实现线程间的通信
条件变量是一种用于线程间通信的同步工具。它们通常与互斥锁一起使用,以等待某个特定条件的发生。当一个线程需要等待另一个线程完成某些操作时,它可以等待条件变量,而当该条件满足时,另一个线程会通知等待的线程继续执行。
生产者-消费者问题的改进版
我们可以通过条件变量来优化之前的生产者-消费者例子。这样可以减少不必要的上下文切换,并且使代码逻辑更加清晰。
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>#define BUFFER_SIZE 5int buffer[BUFFER_SIZE];
int count = 0, in = 0, out = 0;// 条件变量和互斥锁
pthread_mutex_t mutex;
pthread_cond_t not_full, not_empty;void* producer(void* arg) {while (1) {int item = rand() % 100; // 生产一项pthread_mutex_lock(&mutex);// 等待有空位while (count == BUFFER_SIZE)pthread_cond_wait(¬_full, &mutex);// 添加到缓冲区buffer[in] = item;in = (in + 1) % BUFFER_SIZE;++count;printf("Produced: %d\n", item);// 通知消费者pthread_cond_signal(¬_empty);pthread_mutex_unlock(&mutex);sleep(rand() % 3); // 模拟延迟}
}void* consumer(void* arg) {while (1) {pthread_mutex_lock(&mutex);// 等待有商品while (count == 0)pthread_cond_wait(¬_empty, &mutex);// 从缓冲区移除int item = buffer[out];out = (out + 1) % BUFFER_SIZE;--count;printf("Consumed: %d\n", item);// 通知生产者pthread_cond_signal(¬_full);pthread_mutex_unlock(&mutex);sleep(rand() % 3); // 模拟延迟}
}int main() {// 初始化互斥锁和条件变量pthread_mutex_init(&mutex, NULL);pthread_cond_init(¬_full, NULL);pthread_cond_init(¬_empty, NULL);// 创建线程pthread_t producers[3], consumers[2];for (int i = 0; i < 3; ++i) {pthread_create(&producers[i], NULL, producer, NULL);}for (int i = 0; i < 2; ++i) {pthread_create(&consumers[i], NULL, consumer, NULL);}// 主线程等待子线程结束(这里应该有一个合理的退出机制)for (int i = 0; i < 3; ++i) {pthread_join(producers[i], NULL);}for (int i = 0; i < 2; ++i) {pthread_join(consumers[i], NULL);}// 销毁互斥锁和条件变量pthread_mutex_destroy(&mutex);pthread_cond_destroy(¬_full);pthread_cond_destroy(¬_empty);return 0;
}
概念 4:无锁编程(Lock-free Programming)
无锁编程是一种避免使用锁来进行线程同步的方法。它通过原子操作(如CAS, Compare And Swap)来确保数据的一致性,而不需要阻塞其他线程。这种方法可以提高系统的性能,尤其是在多核处理器上,因为减少了线程之间的等待时间。
无锁数据结构的例子包括无锁栈、队列等。由于无锁编程复杂度较高,容易出错,所以通常只会在对性能要求极高的情况下使用,并且需要开发者对底层硬件和内存模型有深入了解。
同步机制的选择
选择合适的同步机制取决于你的具体需求:
- 互斥锁适合于保护共享资源,防止多个线程同时访问。
- 信号量适用于控制对有限数量资源的访问,或者限制并发线程的数量。
- 条件变量常用于线程间的协调,允许线程等待直到满足某个条件。
- 无锁编程适用于需要极高性能且可以接受增加开发复杂度的情况。
在实际应用中,你可能需要组合使用这些机制来达到最佳效果。例如,你可以使用条件变量配合互斥锁来实现复杂的线程同步逻辑,或者用信号量来管理资源池。正确选择和使用同步原语是编写高效、可靠的多线程程序的关键。
相关文章:
第25章 汇编语言--- 信号量与互斥锁
信号量(Semaphore)和互斥锁(Mutex,全称Mutual Exclusion Object)是两种用于管理对共享资源的访问的同步机制。它们在多线程或多进程编程中非常重要,可以确保同一时间只有一个线程或进程能够访问特定的资源&…...
写个自己的vue-cli
写个自己的vue-cli 1.插件代码2. 发布流程3. 模板代码讲解3.1 vue2模板的运行流程:3.2 vue3模板的运行流程: 1.插件代码 写一个自己的vue-cli插件 插件地址:插件地址 流程: 实现简单版 vue-cli 步骤文档1. 项目初始化 - 创建项目文件夹 qsl-vue-cli - …...
使用new Vue创建Vue 实例并使用$mount挂载到元素上(包括el选项和$mount区别)
new Vue({...}) 是创建一个新的 Vue 实例的方式。你可以通过传递一个选项对象来配置这个实例。常见的选项包括: •data:定义组件的数据属性。 •el:指定 Vue 实例应该挂载到哪个 DOM 元素上(通常是一个选择器字符串,如…...
【理论】测试框架体系TDD、BDD、ATDD、MBT、DDT介绍
一、测试框架是什么 测试框架是一组用于创建和设计测试用例的指南或规则。框架由旨在帮助 QA 专业人员更有效地测试的实践和工具的组合组成。 这些指南可能包括编码标准、测试数据处理方法、对象存储库、存储测试结果的过程或有关如何访问外部资源的信息。 A testing framewo…...
机器学习全流程解析:数据导入到服务上线全阶段介绍
目录 1. 数据导入 2. 数据预处理 3. 超参数搜索与优化 4. 模型训练 5. 模型评估 6. 模型压缩与优化 7. 模型注册与版本管理 8. 服务上线与部署 总结 1. 数据导入 数据源:数据库、文件系统、API等。数据格式:CSV、JSON、SQL 数据库表、Parquet …...
shell脚本练习
1、shell 脚本写出检测 /tmp/size.log 文件如果存在显示它的内容,不存在则创建一个文件将创建时间写入。 if [ -f /tmp/size.log ];thencat /tmp/size.logelsestat exist.sh | awk -F: "NR5" > /tmp/size.logfi 2、写一个 shel1 脚本,实现批量添加…...
金山WPS Android面试题及参考答案
说说你所知道的所有集合?并阐述其内部实现。 在 Android 开发(Java 语言基础上)中有多种集合。 首先是 List 集合,主要包括 ArrayList 和 LinkedList。 ArrayList 是基于数组实现的动态数组。它的内部有一个数组来存储元素,当添加元素时,如果数组容量不够,会进行扩容操作…...
分类模型为什么使用交叉熵作为损失函数
推导过程 让推理更有体感,进行下面假设: 假设要对猫、狗进行图片识别分类假设模型输出 y y y,是一个几率,表示是猫的概率 训练资料如下: x n x^n xn类别 y ^ n \widehat{y}^n y n x 1 x^1 x1猫1 x 2 x^2 x2猫1 x …...
高等数学学习笔记 ☞ 单调性、凸凹性、极值、最值、曲率
1. 单调性 1. 单调性定义:设函数在区间上有定义,对于区间上任意两点,若: ①:当时,恒有,则称函数在区间上单调递增。 ②:当时,恒有,则称函数在区间上单调递减…...
【操作系统】详解操作系统及其结构
考察频率难度40%--60%⭐⭐ 这又是一类面试考察题,是关于操作系统的一些概念问题,很少会单独拎出来作为一个问题进行提问,但却是必须要掌握的。因为如果这个你不会,其他的问题就已经没有回答的必要了。 什么是操作系统࿱…...
primitive 的 Appearance编写着色器材质
import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体,Cesium.RectangleGeometry:几何体,Rectangle:矩形 let rectGeometry new…...
自动化测试框架搭建-接口数据结构设计
目的 确认数据库如何保存接口数据,既有扩展性,数据又全又好用 根据用途设计数据库字段 区分环境:可以明确当前接口自动化用例,是在哪个环境需要执行的 模块:微服务架构,不同测试同学负责不同的模块&…...
Python自学 - 使用自定义异常
<< 返回目录 1 Python自学 - 使用自定义异常 在Python中, 不仅可以使用内置异常,用户还可以创建自己的异常。自定义异常需要继承自Exception类或其子类,如下是一个自定义异常示例: 示例1:一个简单的自定义异常…...
微信小程序-Docker+Nginx环境配置业务域名验证文件
在实际开发或运维工作中,我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件,用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx,就需要将该验证文件正确地映射(挂载)到容器中,并…...
“AI智能服务平台系统,让生活更便捷、更智能
大家好,我是资深产品经理老王,今天咱们来聊聊一个让生活变得越来越方便的高科技产品——AI智能服务平台系统。这个系统可是现代服务业的一颗璀璨明珠,它究竟有哪些魅力呢?下面我就跟大家伙儿闲聊一下。 一、什么是AI智能服务平台系…...
【PPTist】插入形状、插入图片、插入图表
一、插入形状 插入形状有两种情况,一种是插入固定的形状, 一种是插入自定义的形状。 插入固定的形状时,跟上一篇文章 绘制文本框 是一样一样的,都是调用的 mainStore.setCreatingElement() 方法,只不多传的类型不一…...
云集电商:数据库的分布式升级实践|OceanBase案例
电商行业对数据库有哪些需求 云集电商作为一家传统电商企业,业务涵盖了美妆个护、服饰、水果生鲜、健康保健等多个领域,在创立四年后在纳斯达克上市(股票代码:YJ)。与京东、淘宝、拼多多等电商平台不同,云…...
OOM排查思路
K8S 容器的云原生生态,改变了服务的交付方式,自愈能力和自动扩缩等功能简直不要太好用。 有好的地方咱要夸,不好的地方咱也要说,真正的业务是部署于容器内部,而容器之外,又有一逻辑层 Pod 。 对于容器和…...
Q_OBJECT宏报错的问题
在Qt中继承QObject,并且加上Q_OBJECT宏,有时候会报错,比如我的错误: error: debug/httpmgr.o:httpmgr.cpp:(.rdata$.refptr._ZTV7HttpMgr[.refptr._ZTV7HttpMgr]0x0): undefined reference to vtable for HttpMgr 意思是没有虚…...
iOS - 关联对象
详细总结 Objective-C 的关联对象功能: 1. 基本使用 // 1. 设置关联对象 objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);// 2. 获取关联对象 id objc_getAssociatedObject(id object, const void *key);// 3. …...
Linux之进程
Linux之进程 一.进程进程之形ps命令进程状态特殊进程孤儿进程守护进程 进程创建之创建子进程进程特性优先级进程切换(分时操作系统) 二.环境变量三.进程地址空间四.进程终止&进程等待五.进程替换六.自定义shell 本篇博客希望简略的介绍进程ÿ…...
数据库事务
一 事务的概念 为什么要有事务,我们先前没学事务,也能写sql语句,事务的意义是什么? 由来: 是为了服务应用层开发,降低开发难度。假如没有事务,那我们身为开发人员,要处理转账需求,此时一定是有…...
Python statistics 模块
在数据分析和科学计算中,统计学是一个非常重要的工具。 Python 提供了一个内置的 statistics 模块,专门用于处理基本的统计计算。本文将详细介绍 statistics 模块的功能和使用方法,帮助初学者快速掌握如何使用这个模块进行基本的统计分析。 …...
AI知识-TF-IDF技术(Term Frequency-Inverse Document Frequency)
摘要 TF-IDF(Term Frequency-Inverse Document Frequency)是一种常见的统计方法,用于评估一个词对于一个文档集或一个语料库中的其中一份文档的重要性。本文将全面阐述TF-IDF的通俗理解、技术原理、应用场景,并做以总结。 通俗理…...
spring cloud的核心模块有哪些
Spring Cloud 的核心模块就像一套精心设计的工具箱,每个模块都扮演着特定的角色,共同构建起微服务架构的坚实基础。 1. Spring Cloud Netflix(部分组件已迁移或弃用,但仍是理解 Spring Cloud 的重要参考): …...
java_将数据存入elasticsearch进行高效搜索
使用技术简介: (1) 使用Nginx实现反向代理,使前端可以调用多个微服务 (2) 使用nacos将多个服务管理关联起来 (3) 将数据存入elasticsearch进行高效搜索 (4) 使用消息队列rabbitmq进行消息的传递 (5) 使用 openfeign 进行多个服务之间的api调用 参…...
RAG中的文本切分策略详解
RAG中的文本切分策略详解 1. 选择RAG中的文本切分策略 1.1 不同的文本切分策略 1. CharacterTextSplitter - 这是最简单的方法。它默认基于字符(默认为"")来切割,并且通过字符的数量来衡量块的长度 2. RecursiveCharacterTextSplitter - 基于字符列表拆分文本。 …...
1-1 电场基本概念
目录: 目录 目录: 1.0 电荷守恒定律 2.0 互斥与相吸 3.0 电场的概念 4.0 库伦定律 5.0 矢量的概念 1.0 电荷守恒定律 电荷守恒定律是物理学中的一个基本原理,它指出在一个封闭系统内,电荷的总量是保持不变的。这意味着电荷既…...
SpringBoot初始化执行自定义接口
SpringBoot初始化执行自定义接口 直接加载接口的方法上即可 PostConstructpublic void init() {//加载初始化数据}PostConstruct是一个在Java EE 5规范中引入的注解,用于标记在依赖注入完成后需要执行的方法。这个注解定义在javax.annotation包中,而不…...
【Ubuntu与Linux操作系统:一、Ubuntu安装与基本使用】
第1章 Ubuntu安装与基本使用 1.1 Linux与Ubuntu Linux是一种开源、类Unix操作系统内核,拥有高稳定性和强大的网络功能。由于其开源性和灵活性,Linux被广泛应用于服务器、嵌入式设备以及桌面环境中。 Ubuntu是基于Debian的一个流行Linux发行版…...
C++大端小端判断方法
文章目录 大端小端定义判断方法方法一:利用联合体(Union)特性判断方法二:通过指针类型转换判断方法三:利用位运算与移位操作判断方法四:使用系统提供的字节序相关宏(特定平台支持) 联…...
【IO编程】标准IO和文件IO的对比
标准 I/O 和 文件 I/O 是两种常见的输入输出操作方式。它们的核心功能都是处理数据流,但使用场景和实现方式有所不同,适用于不同的需求。 标准 I/O 标准 I/O 是指与标准输入、标准输出和标准错误流(分别为 stdin、stdout 和 stderr…...
C#范围表达式,模式匹配,逆变和协变--11
目录 一.范围表达式 1.概述 2.语法 3.代码示例 4.实现原理 5.应用场景 二.模式匹配 1.概述 2.核心概念 3.常用模式类型 4.Switch表达式 5.使用示例 6.优势 三.逆变和协变 1.概述 2.泛型类型参数的变性 3.协变示例 4.逆变示例 5.注意事项 6.应用场景 总结 一…...
矩阵求逆的几种方式
矩阵求逆的几种方式(以二阶为例) 矩阵求逆的方法有多种,以下是常用的几种方式总结: 1. 行列式公式法 这是最常见的方法,适用于 2 2 2 \times 2 22矩阵。 对于矩阵: Φ [ a b c d ] , \Phi \begin{bma…...
全新市场阶段, Plume 生态不断壮大的 RWAfi 版图
加密市场在 2024 年迎来了新的里程碑。BTC 不仅成功推出 ETF,以 BTC 为代表的主流加密货币还在一系列传统金融机构的推动下逐步与主流金融市场接轨。与此同时,随着特朗普成功当选下一任美国总统,他承诺推出一系列友好的加密政策,并…...
HTTPS SSL/TLS 工作流程
目录 一、HTTP/HTTPS 简介1、HTTP协议相关内容2、HTTPS协议3、HTTP版本差异: 二、HTTPS 协议工作流程解析1. 客户端请求 SSL 握手2. 服务端接收 SSL 握手连接3. TLS 握手中的密钥协商4. HTTP 数据的加密与解密5. 安全性保障 三、HTTPS 协议的相关知识拓展1. TLS 与 …...
基于异步IO的io_uring
基于异步IO的io_uring 1. io_uring的实现原理 io_uring使用了一种异步IO机制,它通过一对环形缓冲区(ring buffer)实现用户态于内核态之间的高效通信,用户只需将IO请求放入提交队列,当内核完成IO请求时,会将结果放入完成队列&…...
【redis】centos7下安装redis7
在CentOS 7下安装Redis7可以通过以下两种方法实现:手动编译安装和使用YUM进行安装。 CentOS 7系统的环境和版本: $ cat /etc/centos-release CentOS Linux release 7.9.2009 (Core)手动编译安装 参考官方文档:https://redis.io/docs/lates…...
信息系统项目管理-采购管理-采购清单示例
序号类别产品/服务名称规格/功能描述数量备注1硬件服务器高性能处理器,大容量存储10HP、DELL2网络设备高速路由器和交换机10华为3工作站多核处理器,高分辨率显示器25国产设备4移动检查设备手持式移动检查仪,可连接云平台30国产设备5打印机和扫…...
python 代码使用 DeepXDE 库实现了一个求解二维非线性偏微分方程(PDE)的功能
import deepxde as dde import numpy as np import matplotlib.pyplot as plt import tensorflow as tf# 设置时空计算域 Lx 1 # x 范围从 0 到 1 Ly 1 # y 范围从 0 到 1 Lt 0.05 # t 范围从 0 到 0.05 geom dde.geometry.Rectangle([0, 0], [Lx, Ly]) # 空间域 timed…...
后端技术选型 sa-token校验学习 下 结合项目学习 后端鉴权
目录 后端注册拦截器 实现对 WebMvcConfigurer 接口的类实现 静态变量 方法重写 注册 Spring Framework拦截器 Sa-Token中SaServletFilter拦截器 思考 为什么使用两个拦截器 1. Spring Framework 拦截器 2. SaServletFilter 为什么要注册两个拦截器? 总结 …...
继承(8)
大家好,今天我们来学习一下继承方式相关的知识,有助于我们对java的继承有更深的了解,话不多说,来看。 1.10 继承方式 在现实生活中,事物之间的关系是非常复杂,灵活多样。 Java中支持以下几种继承方式: 单继承: 多层…...
深度学习-图神经网络-超图概念及在Hyper-YOLO的应用(小白也看懂)
为什么需要超图? 在一个复杂系统中,某些节点(实体)之间的互动可能不是仅限于两个节点之间的关系,而是多个节点同时参与的更复杂的关系(超边)。简单说就是为了更好的描述事物之间的关系…...
django基于Python的校园个人闲置物品换购平台
Django 基于 Python 的校园个人闲置物品换购平台 一、平台概述 Django 基于 Python 的校园个人闲置物品换购平台是专为校园师生打造的一个便捷、环保且充满活力的线上交易场所。它借助 Django 这一强大的 Python Web 开发框架,整合了校园内丰富的闲置物品资源&…...
opencv的NLM去噪算法
NLM(Non-Local Means)去噪算法是一种基于图像块(patch)相似性的去噪方法。其基本原理是: 图像块相似性:算法首先定义了一个搜索窗口(search window),然后在该窗口内寻找…...
嵌入式系统中的 OpenCV 与 OpenGLES 协同应用
🎬 秋野酱:《个人主页》 🔥 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 一、OpenCV 在嵌入式中的基石地位二、OpenGLES 为嵌入式图形渲染赋能三、二者协同的精妙之处四、面临的挑战与应对策略 在嵌入式开…...
第三十六章 Spring之假如让你来写MVC——拦截器篇
Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…...
DDD - 如何运用 DDD 进行数据库设计
文章目录 Pre概述领域对象持久化的思想领域模型的设计传统的 4 种关系1. 一对一关系2. 多对一关系3. 一对多关系4. 多对多关系 继承关系的 3 种设计1. 继承关系的第一种方案:整个父类与子类都写入一张表2. 继承关系的第二种方案:各子类各自对应各自的表3…...
OSPF - 特殊报文与ospf的机制
👠1 携带FA地址的5类LSA 除去7类转5类的LSA会携带FA地址,还有一种情况会有FA地址 FA地址:forwarding address 转发地址,解决次优路径,避免环路5类LSA FA地址不为0,则直接通过FA地址去往目标网段 FA地址为0,…...
VSCode 插件
VSCode 插件 1. GitHub Copilot - AI 代码助手 功能:根据上下文提供实时代码补全,支持自然语言转代码,提供符合现代编程规范的建议。进阶技巧: 使用快捷键 Alt ] 切换多个建议。写注释时,描述业务逻辑而不是具体实现…...