redis分布式锁的原子保证

2023-05-16

分布式锁的原子保证

  • 背景提出:分布式锁的原子性还能得到保证吗?
  • 分布式锁的加锁与解锁命令是什么?
  • 所以核心问题到底是什么?
  • 客户端的一条命令是如何执行的呢?
  • 两个机制是在这个过程的什么阶段发货作用呢?会不会影响命令执行的原子性?
    • IO 多路复用机制
    • 多 IO 线程机制
  • 源码阅读时刻
    • 命令读取阶段:readQueryFromClient 函数
    • 命令解析阶段:processInputBuffer函数
    • 命令执行阶段:processCommand 函数
    • 结果返回阶段:addReply 函数
    • IO 多路复用对命令原子性保证的影响
    • 多 IO 线程对命令原子性保证的影响
  • 灵魂拷问

背景提出:分布式锁的原子性还能得到保证吗?

当有多个客户端并发访问某个共享资源时,比如要修改数据库中的某条记录,为了避免记录修改冲突,我们可以让所有客户端从 Redis 上获取分布式锁,只有拿到锁的客户端才能操作共享资源。那么,对于分布式锁来说,它实现的关键就是要保证加锁和解锁两个操作是原子操作,这样才能保证多客户端访问时锁的正确性

Redis 能通过事件驱动框架同时捕获多个客户端的可读事件,也就是命令请求。此外,在 Redis 6.0 版本中,多个 IO 线程会被用于并发地读取或写回数据。而既然如此,就可以来思考一个问题:分布式锁的原子性还能得到保证吗?

分布式锁的加锁与解锁命令是什么?

对于分布式锁的加锁操作来说,我们可以使用 Redis 的 SET 命令


SET lockKey uid EX expireTime NX

其中,lockKey 是锁的名称,uid 是客户端可以用来唯一标记自己的 ID,expireTime 是这个 key 所代表的锁的过期时间,当这个过期时间到了之后,这个 key 会被删除,相当于锁被释放了,这样就避免了锁一直无法释放的问题。

对于解锁,可以使用 Lua 脚本来完成


if redis.call("get",lockKey) == uid then
   return redis.call("del",lockKey)
else
   return 0
end

Lua 脚本会以 EVAL 命令的形式在 Redis server 中执行。客户端会使用 GET 命令读取锁对应 key 的 value,并判断 value 是否等于客户端自身的 ID。如果等于,就表明当前客户端正拿着锁,此时可以执行 DEL 命令删除 key,也就是释放锁;如果 value 不等于客户端自身 ID,那么该脚本会直接返回。

所以核心问题到底是什么?

无论是加锁的 SET 命令,还是解锁的 Lua 脚本和 EVAL 命令,在有 IO 多路复用时,会被同时执行吗?或者当我们使用了多 IO 线程后,会被多个线程同时执行吗?

客户端的一条命令是如何执行的呢?

在这里插入图片描述

两个机制是在这个过程的什么阶段发货作用呢?会不会影响命令执行的原子性?

IO 多路复用机制

在这里插入图片描述
虽然 Redis 使用了 IO 多路复用机制,但是该机制只是一次性获取多个就绪的 socket 描述符,对应了多个发送命令请求的客户端。而 Redis 在主 IO 线程中,还是逐一来处理每个客户端上的命令的,所以命令执行的原子性依然可以得到保证。

多 IO 线程机制

在这里插入图片描述
当使用了 Redis 6.0 版本后,命令处理过程中的读取、解析和结果写回,就由多个 IO 线程来处理了。也不用担心,多个 IO 线程只是完成解析第一个读到的命令,命令的实际执行还是由主 IO 线程处理。当多个 IO 线程在并发写回结果时,命令就已经执行完了,不存在多 IO 线程冲突的问题。

可以看到,多 IO 线程实际并不会加快命令的执行,而是只会将读取解析命令并行化执行,以及写回结果并行化执行,并且读取解析命令还是针对收到的第一条命令。实际上,这一设计考虑还是由于网络 IO 需要加速处理。那么,如果命令执行本身成为 Redis 运行时瓶颈了,你其实可以考虑使用 Redis 切片集群来提升处理效率。

源码阅读时刻

现在我们知道,Redis server 一旦和一个客户端建立连接后,就会在事件驱动框架中注册可读事件,这就对应了客户端的命令请求。而对于整个命令处理的过程来说,我认为主要可以分成四个阶段,它们分别对应了 Redis 源码中的不同函数

命令读取阶段:readQueryFromClient 函数

在这里插入图片描述

readQueryFromClient 函数会从客户端连接的 socket 中,读取最大为 readlen 长度的数据,readlen 值大小是宏定义 PROTO_IOBUF_LEN。该宏定义是在server.h文件中定义的,默认值为 16KB。

在这里插入图片描述

紧接着,readQueryFromClient 函数会根据读取数据的情况,进行一些异常处理,比如数据读取失败或是客户端连接关闭等。此外,如果当前客户端是主从复制中的主节点,readQueryFromClient 函数还会把读取的数据,追加到用于主从节点命令同步的缓冲区中。

在这里插入图片描述

最后,readQueryFromClient 函数会调用 processInputBuffer函数,这就进入到了命令处理的下一个阶段,也就是命令解析阶段。
请添加图片描述

命令解析阶段:processInputBuffer函数

在这里插入图片描述

processInputBufferAndReplicate 函数(在networking.c文件中)会根据当前客户端是否有 CLIENT_MASTER 标记,来执行两个分支。

这个分支对应了客户端没有 CLIENT_MASTER 标记,也就是说当前客户端不属于主从复制中的主节点。那么,processInputBufferAndReplicate 函数会直接调用 processInputBuffer(在 networking.c 文件中)函数,对客户端输入缓冲区中的命令和参数进行解析。

这个分支对应了客户端有 CLIENT_MASTER 标记,也就是说当前客户端属于主从复制中的主节点。那么,processInputBufferAndReplicate 函数除了调用 processInputBuffer 函数,解析客户端命令以外,它还会调用 replicationFeedSlavesFromMasterStream 函数(在replication.c文件中),将主节点接收到的命令同步给从节点。

在这里插入图片描述

命令解析实际是在 processInputBuffer 函数中执行的
首先,processInputBuffer 函数会执行一个 while 循环,不断地从客户端的输入缓冲区中读取数据。然后,它会判断读取到的命令格式,是否以“*”开头。
在这里插入图片描述

如果命令是以“*”开头,那就表明这个命令是 PROTO_REQ_MULTIBULK 类型的命令请求,也就是符合 RESP 协议(Redis 客户端与服务器端的标准通信协议)的请求。
那么,processInputBuffer 函数就会进一步调用 processMultibulkBuffer(在 networking.c 文件中)函数,来解析读取到的命令。

而如果命令不是以“*”开头,那则表明这个命令是 PROTO_REQ_INLINE 类型的命令请求,并不是 RESP 协议请求。这类命令也被称为管道命令,命令和命令之间是使用换行符“\r\n”分隔开来的。

比如,我们使用 Telnet 发送给 Redis 的命令,就是属于 PROTO_REQ_INLINE 类型的命令。在这种情况下,processInputBuffer 函数会调用 processInlineBuffer(在 networking.c 文件中)函数,来实际解析命令。

在这里插入图片描述

这样,等命令解析完成后,processInputBuffer 函数就会调用 processCommandAndResetClient 函数,开始进入命令处理的第三个阶段,也就是命令执行阶段。
请添加图片描述

命令执行阶段:processCommand 函数

请添加图片描述
首先,我们要知道,processCommand 函数是在server.c文件中实现的。它在实际执行命令前的主要逻辑可以分成三步:

  • 第一步,processCommand 函数会调用 moduleCallCommandFilters 函数(在module.c文件),将 Redis 命令替换成 module 中想要替换的命令
  • 第二步,processCommand 函数会判断当前命令是否为 quit 命令,并进行相应处理。
  • 第三步,processCommand 函数会调用 lookupCommand 函数,在全局变量 server 的 commands 成员变量中查找相关的命令。

在这里插入图片描述

这里,你需要注意下,全局变量 server 的 commands 成员变量是一个哈希表,它的定义是在server.h文件中的 redisServer 结构体里面,如下所示:

在这里插入图片描述

另外,commands 成员变量的初始化是在 initServerConfig 函数中,通过调用 dictCreate 函数完成哈希表创建,再通过调用 populateCommandTable 函数,将 Redis 提供的命令名称和对应的实现函数,插入到哈希表中的。

在这里插入图片描述

而这其中的 populateCommandTable 函数,实际上是使用到了 redisCommand 结构体数组 redisCommandTable。

在这里插入图片描述

redisCommandTable 数组是在 server.c 文件中定义的,它的每一个元素是一个 redisCommand 结构体类型的记录,对应了 Redis 实现的一条命令。也就是说,redisCommand 结构体中就记录了当前命令所对应的实现函数是什么。

比如,以下代码展示了 GET 和 SET 这两条命令的信息,它们各自的实现函数分别是 getCommand 和 setCommand。当然,如果你想进一步了解 redisCommand 结构体,也可以去看下它的定义,在 server.h 文件当中。

在这里插入图片描述

好了,到这里,你就了解了 lookupCommand 函数会根据解析的命令名称,在 commands 对应的哈希表中查找相应的命令。

那么,一旦查到对应命令后,processCommand 函数就会进行多种检查,比如命令的参数是否有效、发送命令的用户是否进行过验证、当前内存的使用情况,等等。这部分的处理逻辑比较多,你可以进一步阅读 processCommand 函数来了解下。

这样,等到 processCommand 函数对命令做完各种检查后,它就开始执行命令了。它会判断当前客户端是否有 CLIENT_MULTI 标记,如果有的话,就表明要处理的是 Redis 事务的相关命令,所以它会按照事务的要求,调用 queueMultiCommand 函数将命令入队保存,等待后续一起处理。
在这里插入图片描述

而如果没有,processCommand 函数就会调用 call 函数来实际执行命令了。以下代码展示了这部分的逻辑,你可以看下。
在这里插入图片描述

这里你要知道,call 函数是在 server.c 文件中实现的,它执行命令是通过调用命令本身,即 redisCommand 结构体中定义的函数指针来完成的。而就像我刚才所说的,每个 redisCommand 结构体中都定义了它对应的实现函数,在 redisCommandTable 数组中能查找到。

因为分布式锁的加锁操作就是使用 SET 命令来实现的,所以这里,我就以 SET 命令为例来介绍下它的实际执行过程。

在这里插入图片描述
SET 命令对应的实现函数是 setCommand,这是在t_string.c文件中定义的。setCommand 函数首先会对命令参数进行判断,比如参数是否带有 NX、EX、XX、PX 等这类命令选项,如果有的话,setCommand 函数就会记录下这些标记。

在这里插入图片描述

然后,setCommand 函数会调用 setGenericCommand 函数,这个函数也是在 t_string.c 文件中实现的。setGenericCommand 函数会根据刚才 setCommand 函数记录的命令参数的标记,来进行相应处理。比如,如果命令参数中有 NX 选项,那么,setGenericCommand 函数会调用 lookupKeyWrite 函数(在db.c文件中),查找要执行 SET 命令的 key 是否已经存在。

在这里插入图片描述

如果这个 key 已经存在了,那么 setGenericCommand 函数就会调用 addReply 函数,返回 NULL 空值,而这也正是符合分布式锁的语义的。

好,那么如果 SET 命令可以正常执行的话,也就是说命令带有 NX 选项但是 key 并不存在,或者带有 XX 选项但是 key 已经存在,这样 setGenericCommand 函数就会调用 setKey 函数(在 db.c 文件中)来完成键值对的实际插入,如下所示:

在这里插入图片描述

然后,如果命令设置了过期时间,setGenericCommand 函数还会调用 setExpire 函数设置过期时间。最后,setGenericCommand 函数会调用 addReply 函数,将结果返回给客户端

结果返回阶段:addReply 函数

addReply 函数是在 networking.c 文件中定义的。它的执行逻辑比较简单,主要是调用 prepareClientToWrite 函数,并在 prepareClientToWrite 函数中调用 clientInstallWriteHandler 函数,将待写回客户端加入到全局变量 server 的 clients_pending_write 列表中。

然后,addReply 函数会调用 _addReplyToBuffer 等函数(在 networking.c 中),将要返回的结果添加到客户端的输出缓冲区中。

IO 多路复用对命令原子性保证的影响

首先你要知道,IO 多路复用机制是在 readQueryFromClient 函数执行前发挥作用的。它实际是在事件驱动框架中调用 aeApiPoll 函数,获取一批已经就绪的 socket 描述符。然后执行一个循环,针对每个就绪描述符上的读事件,触发执行 readQueryFromClient 函数。

这样一来,即使 IO 多路复用机制同时获取了多个就绪 socket 描述符,在实际处理时,Redis 的主线程仍然是针对每个事件逐一调用回调函数进行处理的。而且对于写事件来说,IO 多路复用机制也是针对每个事件逐一处理的。

下面的代码展示了 IO 多路复用机制通过 aeApiPoll 函数获取一批事件,然后逐一处理的逻辑,你可以再看下。


numevents = aeApiPoll(eventLoop, tvp);

for (j = 0; j < numevents; j++) {
   aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
   if (!invert && fe->mask & mask & AE_READABLE) {
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                fired++;
   }

所以这也就是说,即使使用了 IO 多路复用机制,命令的整个处理过程仍然可以由 IO 主线程来完成,也仍然可以保证命令执行的原子性

多 IO 线程对命令原子性保证的影响

我们知道,多 IO 线程可以执行读操作或是写操作。那么,对于读操作来说,readQueryFromClient 函数会在执行过程中,调用 postponeClient 将待读客户端加入 clients_pending_read 等待列表

然后,待读客户端会被分配给多 IO 线程执行,每个 IO 线程执行的函数就是 readQueryFromClient 函数,readQueryFromClient 函数会读取命令,并进一步调用 processInputBuffer 函数解析命令

在 Redis 6.0 版本中,processInputBuffer 函数中新增加了一个判断条件,也就是当客户端标识中有 CLIENT_PENDING_READ 的话,那么在解析完命令后,processInputBuffer 函数只会把客户端标识改为 CLIENT_PENDING_COMMAND,就退出命令解析的循环流程了。
在这里插入图片描述

此时,processInputBuffer 函数只是解析了第一个命令,也并不会实际调用 processCommand 函数来执行命令

在这里插入图片描述

这样,等到所有的 IO 线程都解析完了第一个命令后,IO 主线程中执行的 handleClientsWithPendingReadsUsingThreads 函数,会再调用 processCommandAndResetClient 函数执行命令,以及调用 processInputBuffer 函数解析剩余命令,所以现在,你就可以知道,即使使用了多 IO 线程,其实命令执行这一阶段也是由主 IO 线程来完成的,所有命令执行的原子性仍然可以得到保证,也就是说分布式锁的原子性也仍然可以得到保证。

再来看下写回数据的流程。

在这个阶段,addReply 函数是将客户端写回操作推迟执行的,而此时 Redis 命令已经完成执行了,所以,即使有多个 IO 线程在同时将客户端数据写回,也只是把结果返回给客户端,并不影响命令在 Redis server 中的执行结果。也就是说,即使使用了多 IO 线程写回,Redis 同样可以保证命令执行的原子性。

灵魂拷问

如果将命令处理过程中的命令执行也交给多 IO 线程执行,你觉得除了对原子性会有影响,还会有什么好处或是其他不好的影响吗?

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

redis分布式锁的原子保证 的相关文章

  • 计算机网络课后习题概略

    计算机网络 课后习题 第一章 概述 1 01 计算机网络可以向用户提供哪些服务 xff1f 服务功能或作用数据传输网络间个计算机之间互相进行信息的传递 资源共享进入网络的用户可以对网络中的数据 软件和硬件实现共享 分布处理功能通过网络可以把
  • Kittle案例(Excel输入,生成记录,生成随机数,获取系统信息,排序记录,去除重复记录,替换NULL值)

    一 Excel输入 1 创建转换 将要处理的文件保存到Excel表中 xff0c 然后打开kettle创建一个转换并命名 xff0c 拖入 Excel输入 控件 2 配置Excel输入控件 浏览导入Excel文件 单击 浏览 B 按钮 xf
  • 第七章 数据加载

    一 全量加载 从技术角度来说 xff0c 全量加载比增量加载的操作要简单很多 xff0c 即只需要在数据加载之前 xff0c 将目标数据表进行清空 xff0c 再将源数据表中的数据全部加载到目标表中 案例实现 xff1a 数据装备 xff1
  • 第八章 综合案例——构建DVD租赁商店数据仓库(1)

    一 加载日期数据至日期维度表 操作介绍 xff1a 下面通过Kettle工具加载日期数据至dim date日期维度表 1 打开Kettle工具 xff0c 创建转换 使用Kettle工具 xff0c 创建一个转换load dim date
  • 第八章 综合案例——构建DVD租赁商店数据仓库(2)

    一 加载用户数据至用户维度表 操作介绍 xff1a 通过Kettle工具加载用户数据至用户维度表dim customer 1 打开Kettle工具 xff0c 创建转换load dim customer 使用Kettle工具 xff0c 创
  • C++与python交互库-pybind11的返回值策略 return_value_policy

    pybind11的返回值策略 return value policy 一 返回值策略的必要性二 一个导致crash的例子三 所有的返回值策略的探讨四 补充说明 一 返回值策略的必要性 C 43 43 和python使用根本上就不一样的内存管
  • 第八章 综合案例——构建DVD租赁商店数据仓库(3)

    一 加载演员数据至演员维度表 操作介绍 xff1a 通过Kettle工具加载演员数据至演员维度表dim actor 1 打开Kettle工具 xff0c 创建转换 使用Kettle工具 xff0c 创建一个转换load dim actor
  • 树莓派4b安装python库

    树莓派4b安装python库 树莓派安装好系统后自带的有python2和python3 xff0c 默认是使用python2 xff0c 可以打开树莓派查看 xff1a 输入python 查看默认的python版本 xff1a 我原来默认是
  • SONIC+P4

    SONIC简介 背景 微软于2015年先后发表了SAI和SONIC SONIC产业日益繁荣 xff0c 已超过六十家 创新 SONiC使用了大量现有的开源技术 xff1a Docker Redis Quagga和LLDPD以及自动化配置工具
  • 6.ROS&PX4--Pixhawk无人机+A1激光雷达SLAM部署

    6 ROS amp PX4 无人机A1激光雷达SLAM部署 一 常见2D SLAM算法 Gmapping Hector SLAM Cartographer Gmapping 基于粒子滤波的2D激光雷达SLAM xff0c 构建二维栅格地图
  • Codeblock提示: Can‘t find compiler executable in your configured search path‘s for GNU GCC无法找到编译器

    can t find compiler executable in your configured search path s for GUN GCC Complier的应对办法 今天使用codeblocks 点击Settings Comp
  • Hexo系列 | Hexo安装、启动

    目录 前言 一 Hexo是什么 二 安装node js 三 安装Git 四 检查是否安装成功 五 Hexo安装 启动 前言 用Hexo框架搭建个人博客 xff0c 记录搭建过程 个人博客效果 一 Hexo是什么 Hexo官网 Hexo 是一
  • 【python】python语法糖

    python 语法糖 1 什么是语法糖 x1f36c 语法糖是由编程语言提供的一种可以让我们写出来的代码在不影响语句执行的功能的条件下 xff0c 能够让我们的代码看起来更简洁和美观的一种语法 很多人刚开始接触语法糖的时候 xff0c 总会
  • 因装了python3.6版本导致ubuntu下terminal无法打开

    参考stack overflow xff1a https stackoverflow com questions 61144232 updated to python 3 8 terminal wont open fixed but det
  • E: 无法下载 https://mirrors.tuna.tsinghua.edu.cn/kali/dists/kali-rolling/main/Contents-amd64.gz 文件尺寸不符

    本人是新手 这个问题困扰了我很久 在网上也查了很久 答案千篇一律 我写下我的解决方法 希望对你有帮助 问题 E 无法下载 https mirrors tuna tsinghua edu cn kali dists kali rolling
  • 软件工程知识点总结

    1 软件的本质 1 1 软件是什么 xff1a 软件是能够完成预定功能和性能 xff0c 并对相应数据进行加工的程序和描述程序操作的文档 1 2 软件的特点 软件是设计开发的 xff0c 而不是传统意义上生产制造的软件不会 磨损 虽然整个工
  • C++ lambda表达式如何理解?编译器做了什么

    lambda表达式在C 43 43 11中引入 xff0c 用lambda表达式表示匿名函数非常方便 xff0c 语法很简单 xff0c 而且可以使代码更紧凑 xff0c 更易于阅读 一个例子 xff1a span class token
  • mysql主从复制&读写分离

    文章目录 本文内容前言1 MySQL主从复制1 1 介绍1 2 搭建1 2 1 准备工作1 2 2 主库配置1 2 3 从库配置 1 3 测试 2 读写分离案例2 1 背景介绍2 2 ShardingJDBC介绍2 3 数据库环境2 4 初
  • 输出所有质数

    1 实验目的 了解数字 布尔值 字符串 列表 元组 字典 集合等Python常用的变量类型 熟练掌握运算符 内置函数的用法 熟练掌握元素常用方法 切片和列表推导式 2 实验内容 xff08 一 xff09 输出所有质数 输入一个整数 xff
  • 声音克隆项目实现

    声音克隆 原理介绍 第一个模块 xff1a 收到说话人音频 xff0c 然后转成这样一个低维表征向量speaker embedding xff0c xff08 这个向量富含说话信息 xff09 实现方式 xff1a 由于是无监督学习 xff

随机推荐

  • c语言枚举类型

    定义 在c语言中 xff0c 枚举类型定义用关键字enum标识 xff0c 形式为 xff1a enum 名字 枚举数据表 xff08 用 xff0c 隔开 xff09 xff1b 注意 xff1a xff08 1 xff09 enum是关
  • 交换机二三层协议及其详细解答

    交换机二三层协议及其详细解答 概述二层协议三层协议以太网协议示例代码 CSMA CD协议示例代码 IP协议示例代码 概述 交换机是网络设备的一种 xff0c 主要用于连接多个网络设备 xff0c 以实现网络通信和数据传输 交换机的协议分为两
  • C++ MVC模式

    概述 C 43 43 是一种流行的编程语言 xff0c 它可以用于构建各种类型的应用程序 xff0c 包括Web应用程序 桌面应用程序和移动应用程序 在这里 xff0c 我将为您介绍C 43 43 中的MVC模式 xff0c 以及如何在C
  • C++ 线程操作

    C 43 43 线程操作 概述 线程是 Linux 操作系统中的一种轻量级进程 xff0c 用于实现并发执行 线程可以共享进程的资源 xff0c 包括内存 文件句柄等 在 C 43 43 中 xff0c 线程操作由标准库提供支持 xff0c
  • QT常用类型字节数组QByteArray及其基本使用

    目录 概述特点常见函数QByteArray append xff1a QByteArray insert xff1a QByteArray replace xff1a QByteArray remove xff1a QByteArray t
  • QT图像处理类QImage常见使用方法

    目录 概述加载和保存图像图像缩放和旋转图像灰度化QImage convertToFormat 图像二值化threshold 函数 loadFromData 从内存加载图像拓展 概述 Qt 是一个跨平台的 C 43 43 库 xff0c 提供
  • pytorch卷积神经网络中间特征层可视化

    前言 在我们使用神经网络的过程中 xff0c 经常会好奇中间的网络到底学到了些什么 xff0c 所以常常想用可视化的方法来输出这些特征层 xff0c 所以惊天带大家用一个简易的网络来输出这些特征层 搭建网络 span class token
  • C++11 lambda表达式在回调函数中的使用

    C 43 43 11 lambda表达式在回调函数中的使用 一 lambda表达式在C 43 43 异步框架中的应用1 一个boost asio的例子2 C 43 43 http框架cutelyst在异步执行PostgreSQL数据库sql
  • MySQL知识点总结(一)

    文章目录 前言最左前缀匹配规则Mysql中sql语句执行太慢 xff0c 是什么原因 xff0c 怎么解决 xff0c 用什么命令查看如何查看是否用到索引为啥加了索引会变快判断是否走联合索引总结 前言 本文来介绍数据库啦 最左前缀匹配规则
  • 基于 NCC/灰度信息 的模板匹配算法(QT + Opencv + C++),10ms内获取匹配结果,部分源码

    文后代码 xff0c 优化效果图结尾处 xff0c 最快3ms得到匹配结果 NCC xff0c 全称为Normalized Cross Correlation xff0c 即归一化互相关系数 xff0c 在模板匹配中使用的非常非常广泛 xf
  • 网络应用基础 ——(2023新星计划文章二)

    一 xff0c TCP报头解析 数据打包与解析直观图 xff1a 1 0报文与报文字段 1 0 1 报文 报文是计算机网络中交换信息的基本单位 xff0c 是一种用于在网络中传递数据的结构化数据格式 在网络通信中 xff0c 数据会被封装成
  • ROS中工作空间和功能包的创建以及发布者Publisher的实现

    最近刚刚开始学习ROS xff0c 对于整个ROS的框架和功能正在一点点的了解 xff0c 跟着B站古月居的 ROS入门21讲 课程 xff0c 在安装好linux和ROS后 xff0c 正式开始ROS的学习 xff0c 动手实践敲代码 x
  • Failed to fetch https://mirrors.tuna.tsinghua.edu.cn/ubuntu//dists/bionic/main/binary-arm64/Packages

    在jeston nano执行 sudo apt update 的时候出现下列错误 xff1a Failed to fetch https mirrors tuna tsinghua edu cn ubuntu dists bionic ma
  • 场景设计法

    目录 一 场景设计法 1 理解 二 场景法的基本设计步骤 三 场景设计法需要掌握的基本知识 1 基本流和备选流 2 测试用例组成 四 优缺点 1 优点 2 缺点 五 使用场景 六 注意 七 实例 1 案例 2 分析需求 xff0c 确定基本
  • 【第一章】计算机网络知识点整理

    文章目录 第一章 概述1 1 计算机网络的定义及其特点1 定义2 计算机网络系统的组成3 功能4 七个典型特点 1 2 互联网概述1 internet 和 Internet 的区别2 互联网基础结构发展的三个阶段 1 3 互联网的组成一 边
  • C# 窗体应用常用基础控件讲解(萌新版)

    C 窗体应用常用基础控件讲解 xff08 适合萌新 xff09 前言 博主这篇文章主要讲解C 窗体应用的几个常用的控件 对新手很友好 xff0c 这几个控件在C 窗体应用中用的频率特别高 xff0c 如果你第一次学C 窗体应用 xff0c
  • 【安装】Ubuntu20.04下安装ROS的完整过程(内含已装好ROS的虚拟机、虚拟机创建过程、ROS安装过程及全过程录屏)

    2022 12 20重制 xff0c 精简流程 xff0c 直接去掉网络问题 现成的虚拟机 为方便大家学习 xff0c 如果安装ROS遇到的问题实在太多 xff0c 也可以直接下载我提供给大家的 已经安装好ROS的Ubuntu虚拟机 xff
  • C++和C的区别

    问 xff1a 能说一下C 43 43 和C的区别吗 xff1f 参考 xff1a 可以从设计思想 语法以及内存管理这三方面来说 1 设计思想上 xff1a C 43 43 是面向对象的语言 xff0c 而C是面向过程的结构化编程语言 2
  • 中文分词之最短路径法和N最短路径

    考虑到汉语分词存在切分歧义消除和未登录词识别两个主要问题 xff0c 因此 xff0c 有专家将分词分成两个阶段 xff1a 1 用分词算法进行粗分2 对粗分的最好结果进行歧义消除和未登录词识别 最短路径法是一种自动分词的算法 它将一句话中
  • redis分布式锁的原子保证

    分布式锁的原子保证 背景提出 分布式锁的原子性还能得到保证吗 分布式锁的加锁与解锁命令是什么 所以核心问题到底是什么 客户端的一条命令是如何执行的呢 两个机制是在这个过程的什么阶段发货作用呢 会不会影响命令执行的原子性 IO 多路复用机制多