C/C++代码静态检测工具PC-Lint常见错误总结
目录
1、PC-Lint 概述
2、PC-lint 常见错误列举
3、PC-Lint报告的语法错误
4、总结
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章正在更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_11931267.html 为了提高代码质量,我们一般会引入一些代码静态检测工具,比如PC-lint和腾讯的TScanCode等,可以帮我们发现代码中存在的一些潜在问题。之前我们项目中使用了PC-Lint,本文大概地总结一个PC-Lint的常报错误,以及这些错误的解决办法。
1、PC-Lint 概述
PC-Lint是一种C/C++静态代码检测工具,通过扫描静态的代码文件(头文件和源文件)进行分析,不仅可以像IDE编译器那样检查出一般的语法错误,还可以检查出那些表面上完全合乎语法要求,但很可能是潜在的、不易发现的问题或错误,比如数组访问越界、内存泄漏、使用未初始化变量等,这样就可以很好地提高软件的质量。
对于这些bug,如果事先没有检测,在软件运行过程中出了问题,通过走读代码或者使用分析工具去分析,可能会花费很长时间才能找到原因。通过使用PC-Lint进行检测,可以在测试开始前就能检测出这些错误,会给测试和开发节省很多的时间。虽然使用PC-Lint会花费一些时间,但这是完全值得的。
另外,PC-lint还支持Effective C++ / More Effective C++中说描述的各种提高效率和防止错误的方法,对照相应的条款,还可以学习与熟悉这些编程要点,提升我们编码的技能。
2、PC-lint 常见错误列举
下面所列举的常见错误只有ID号与简单的描述信息,如果有需要,可以通过ID号在PC-Lint目录下的MsgRef.html文件中查看详细的描述信息。另外本文对这些错误只进行了一些简单的分析,其中部分错误可以参见《Effective C++》上相应的条款。
1)error 1561: (Warning -- Reference initialization causes loss of const/volatile integrity (arg. no. 1))
PC-Lint发出警告:“第一个参数在初始化时会破坏const属性”,双击警告,定位到代码:
TManufacturerInfo *pManufactureList=NULL;
DWORD dwManufactureNum=g_pcCommData->GetAllManufactory(pManufactureList);
查看一下GetAllManufactory函数的声明:
DWORD GetAllManufactory(const TManufacturerInfo*& ptManufactory) const;
参数是const TManufacturerInfo*&,不希望指针所指向的内容被改变,但是我们传入的参数是TManufacturerInfo *,缺少了const限制,导致我们就可以随意改变指针所指向的内容。
2)error 1401: (Warning -- member ‘XXX::YYY' not initialized by constructor);
成员变量没有被初始化,会造成程序一些不可预知的结果,对于这些危害,大家都比较清楚的,但这还是最常犯的一些错误之一,因为大家在开始写代码的时候或以后的维护的时候还会忘记的。
3)error 1729: (Initializer inversion detected for member 'Symbol' );
成员变量在初始化列表的顺序与在类声明中出现的顺序不一致。C++总是按成员变量在类声明中出现的顺序来初始化成员变量的,如果不一致有可能造成一些隐蔽的bug;例如下面的代码:
class A
{
public:A( ):y(100),x(y){}
private:int x;int y;
};
在成员变量初始化时,由于y(100)写在了x(y)之前,我们很可能认为先执行y(100),然后执行x(y),这样x与y的值都初始化为100,但实际上x(y)首先执行,y这时还没有被初始化,x就被初始化为一个随机值。
4)error 1540: (Warning -- Pointer member ' XXX::YYY ' neither freed nor zeroed by destructor -- Effective C++ #6)
在析构函数中一定要把成员指针变量置为空指针,以免产生野指针,并且查看程序中是否有申请空间的操作,如果有一定要用delete或delete[]释放空间,如果没有则一定不要释放该指针指向的空间,把别人申请的空间释放掉,很可能引起错误。
5)error 545: (Warning -- Suspicious use of &)
意思是“错误的使用了&”,定位到代码:
int array[100];
memset(&array, 0, sizeof(array));
原来我们多用了“&”, 改成:
memset(array, 0, sizeof(array));
就行了。其实array与&array都指向了同一个地址,但是要注意 array 和 &array 的类型是不同的。array 相当于 &array[0],而 &array 是一个指向 int[100] 的指针,类型是 int(*)[100]。array + 1指向数组的第二个元素,而&array + 1指向的地址已经超出了本数组的地址范围了。
6)error 616: (Warning -- control flows into case/default)
error 744: (Info -- switch statement has no default)
error 788:(Info -- enum constant 'Symbol' not used within defaulted switch)
这3个警告都是与switch语句有关,都是在使用switch时最容易犯的错误,第一个是当一个case语句后面没有用break,程序会继续执行下一个case语句;第二个是没有写default语句;第三个是当使用enum类型作为switch值时,至少有一个enum变量没有出现在case语句中。
7)error 613: (Warning -- Possible use of null pointer ' 'XXX::YYY ' in left argument to operator '->')
指针有可能为空,使用空指针会使程序崩溃的,在写代码时遇到使用指针时最好还是用if语句判断一下,或者assert断言一下。
8)error 1763: (Info -- Member function 'CXXX::YYY(void) const marked as const indirectly modifies class)
成员函数被声明成const,但是成员变量的值有可能被间接改变,看一下函数的实现:
HTREEITEM CMGTreeCtrl::GetHitItem() const
{return m_TvhitLastClick.hItem;
}
函数被声明成const,表示这个函数不能改变成员变量的值,但当函数返回成员变量的指针或句柄时,成员变量的值就能被外部操作改变,这时要为返回值加上const,以便告诉调用者,不希望返回的指针或句柄所指向的内容的被改变。
9)error 773: (Info -- Expression-like macro 'LIST_DEVICE_MSG_ON_PAGE_CHANGE' not parenthesized)
定位到代码:
#define LIST_DEVICE_MSG_ON_PAGE_CHANGE WM_USER+1
定义宏时没有加括号,当程序预编译时,用WM_USER+1替换到代码中有可能产生非预期的结果,比如:
LIST_DEVICE_MSG_ON_PAGE_CHANGE - LIST_DEVICE_MSG_ON_PAGE_CHANGE
我们当然希望结果是0,但得到的结果却是2。
10)error 424: (Warning -- Inappropriate deallocation (delete) for 'new[]' data -- Effective C++ #5)
使用new[]申请空间,但却使用delete释放空间,造成内存的泄漏,在使用时new与delete配对,new[]与delete[]配对。
11)error 1506: (Warning -- Call to virtual function ' 'XXX::YYY(void)' within a constructor or destructor)
在构造函数或者析构函数中千万不要调用virtual函数,因为在base class构造函数中,virtual函数不会调用derived class中的虚函数,不会带来你所预想的结果。如果一定要使用virtual函数,请使用“类名::virtual函数”表示调用自身的函数,避免造成误解。
12)error 527:(Warning -- Unreachable code at token 'XX')
不可到达的代码。可能是因为前面return掉了,后面的代码就永远不可能执行。通常是因为我们暂时屏蔽掉了某些功能但暂时没有删除其代码导致。可以用#if 0 来屏蔽之:
#if 0 //写出屏蔽原因
代码
#endif
13)error 1925:(Note -- Symbol 'XXX::YYY' is a public data member)
error 1536:(Warning -- Exposing low access member 'CXXX::m_XX')
在类设计时应该将所有成员变量为private,而不是public,因为:
(1)更精确的控制对成员变量的处理。如果成员变量是public的话,每个人都可以读写它,但如果时private的话,我们就可以通过是否提供setterXX、getterXX实现对成员变量的不准访问,只读,只写,读写多种方式。
(2)封装性,将成员变量隐藏在函数接口的背后,为以后修改接口提供方便。
14)error 578: (Warning -- Declaration of symbol ' XXX ' hides symbol ' XXX' (line 891))
当前声明的XXX覆盖了891行声明的XXX,在一个变量的作用域下再次声明了一个同名的变量在语法上当然没有什么错误,但是这样会造成歧异,容易混淆,给别人阅读代码造成困难,不利于以后的维护。
15)error 661: (Warning -- Possible access of out-of-bounds pointer (22 beyond end of data) by operator '[')
数组下标可能越界。
16)error 774: (Info -- Boolean within 'if' always evaluates to False)
If判断语句的值始终为FALSE或者TRUE。
17)error 524: (Warning -- Loss of precision (arg. no. 3) (double to int))
error 732: (Info -- Loss of sign (arg. no. 1) (char to unsigned int))
error 713: (Info -- Loss of precision (arg. no. 1) (unsigned long to long))
error 573: (Warning -- Signed-unsigned mix with divide)
error 574: (Warning -- Signed-unsigned mix with relational)
error 747: (Info -- Significant prototype coercion unsigned long to double)
error 737: (Info -- Loss of sign in promotion from int to unsigned long)
error 734: (Info -- Loss of precision (arg. no. 2) (31 bits to 7 bits))
error 570: (Warning -- Loss of sign (initialization) (int to unsigned long))
error 569: (Warning -- Loss of information (assignment) (32 bits to 31 bits))
error 736: (Info -- Loss of precision (initialization) (55 bits to 32 bits))
与精度丢失和符号错误相关的一些警告。
18)error 539: (Warning -- Did not expect positive indentation from line 529)
代码格式没有对齐,这个时候,先选中没有对齐的代码,然后使用“ALT+F8”键,就可以对齐了。
19)error 715 (Symbol XXX not referenced)
error 752 (local declarator XXX not referenced)
error 550 (Symbol XXX not accessed)
error 529 (Symbol XXX not subsequently referenced)
error 750 (Info -- local declarator 'XXX' macor not referenced)
error 751 (Info -- local typedef 'XXX' not referenced)
代码中声明了一些没有用过的东东。
20)error 685: (Warning -- Relational operator '>=' always evaluates to True)
error 775: (Info -- non-negative quantity cannot be less than zero)
error 568: (Warning -- non-negative quantity is never less than zero)
if 语句中判断一个整数是否小于零, 而正数不可能小于零:
DWORD dwValue = XXX;
if( dwValue >= 0 )
if( dwValue < 0 )
21)其他错误
以下的警告信息也是比较常见的信息,这里只做一些简单的列举:
error 818: (Info -- Pointer parameter 'Symbol' (Location) could be declared as pointing to const)//建议为指针参数加上const属性
error 1762: (Info -- Member function 'Symbol' could be made const)//建议为成员函数加上const属性
error 1746: (Info -- parameter could be made const reference)// 函数参数可以成为const引用
error 1764: (Info -- Reference parameter 'XX' could be declared const ref) //建议为引用参数加上const属性error 1732: (Info -- new in constructor for class 'CXXX' which has no assignment operator)//在类的构造函数中使用new申请了一块内存,但没有提供operator =。
error 1733: (Info -- new in constructor for class 'CXXX' which has no copy constructor )// 在类的构造函数中使用new申请了一块内存,但没有提供copy constructor。
注:在构造函数中使用new申请一块内存,可以推测在类中应该有一个成员变量指针指向该内存。当使用该类的一个对象去克隆另一个对象时,如果该类没有声明copy constructor或operator =,编译器将会自动生成一个默认的copy constructor或operator =,但默认的copy constructor与operator =都是“浅拷贝”,那么这2个对象的指针就指向了同一块内存地址,当一个对象析构时这块内存会被释放,另一个对象就会被破坏。error 749: (Info -- local enumeration constant not ref)//定义了枚举常量却没有使用。
error 506: (Warning -- Constant value Boolean)//布尔值是个常量,导致if()或者while()每次都执行同样的路径。
error 669: (Warning -- Possible data overrun for function memcpy)//可能copy的数据过大
error 765: (Info -- external XXX could be made static)//只用在一个文件中的变量应当声明为staticerror 429: (Warning -- Custodial pointer 'pXXX'has not been freed or returned)//内存可能没有释放
error 1774: (Info -- Could use dynamic_cast to downcast polymorphic type 'CXXX')//建议使用dynamic_cast将一个父类转换成子类。
error 730: (Info -- Boolean argument to function)// 函数参数非 Boolean类型,但却传入了一个Boolean类型的数。
error 1551: (Warning--function may throw exception 'Name' in destructor 'Symbol' ) //析构函数抛异常
3、PC-Lint报告的语法错误
以下列举的警告都是语法错误,基本上都是类型错误、重复定义、没有定义之类的东西,由编译器检查就OK了,不用用PC-lint来检查,一般情况下可以把这些信息屏蔽掉。
error 40: Undeclared identifier//标识符未定义
error 1055: Symbol 'XXX' undeclared, assumed to return int//函数没有定义,假定返回值为int
error 1015: Symbol 'XXX' not found in class //没有定义成员变量'XXX'
error 1013: Symbol 'XXX(OnInitialUpdate)' not a member of class 'YYY(CListView)' //'XXX’不是另一个成员函数
error 746: call to function 'XXX(IsKindOf())' not made in the presence of a prototype) //没有发现函数原型
error 38: Offset of symbol 'XXX' inconsistent (conflicts with other cpp)//
error 113: (Error -- Inconsistent enum declaration)//枚举变量声明的顺序不一致(另一个模块有同名的枚举类型引起)
error 148: Error -- member 'Symbol' previously declared at Location//变量已经声明过了,重复声明
error 1039: (Error -- Symbol 'XX::YY()' is not a member of class 'XX')// YY()不是'XX'的成员函数
error 322: (Error -- Unable to open include file 'xx.h')//不能打开头文件
error 7: (Error -- Unable to open include file 'xx.h') //不能打开头文件
error 1074: (Error -- Expected a namespace identifier)//希望使用命名空间标识符
error 31: (Error -- Redefinition of symbol "XXX" compare with line)//重复定义
error 14: Symbol 'XXX' previously defined//变量重复定义
error 1032: (Error -- Member 'XXX' cannot be called without object)//非static变量只能通过类的一个对象调用
error 58: (Error -- Bad type)指针不能与某个数比较
error 63: (Error -- Expected an lvalue)//需要是一个左值
error 115: (Error -- struct/union not defined)//结构体后者枚举类型没有定义
error 770: (Info -- tag 'Symbol' defined identically at Location)//定义了相同的结构体或者联合体
error 64: type mismatch //类型不匹配
error 526: (Warning -- Symbol 'XXX'没有定义),变量没有定义
error 833: (Info -- Symbol 'XXX' is typed differently in another module)//变量在另一个模块中被定义成不同的类型
error 118: (Error -- Too few arguments )//传入的参数太少
error 55: (Error -- Bad type)//类型错误
error 1046: (Error -- Member 'CXXX::XX', referenced in a static function, requires an object)//非static变量只能不能被static函数调用
4、总结
由于人工Code Review工作很耗精力,我们在程序进行编译测试前使用PC-lint进行代码检查,可以预先发现和规避很多错误,提高代码质量,节省测试时间,规范编码行为。
相关文章:

C/C++代码静态检测工具PC-Lint常见错误总结
目录 1、PC-Lint 概述 2、PC-lint 常见错误列举 3、PC-Lint报告的语法错误 4、总结 VC常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C软件异常排查从入门到…...

概率深度学习建模数据不确定性
https://zhuanlan.zhihu.com/p/568912284理解论文 What uncertainties do we need in Bayesian deep learning for computer vision? (NeurIPS 2017) [1]中的数据不确定性建模,并给出公式推导。论文[1]指出不确定性uncertainty分为随机不确定性(aleator…...

Jenkins自动化部署前后端分离项目 (svn + Springboot + Vue + maven)有图详解
1. 准备工作 本文的前后端分离项目,技术框架是: Springboot Vue Maven SVN Redis Mysql Nginx JDK 所以首先需要安装以下: 在腾讯云服务器OpenCLoudOS系统中安装jdk(有图详解) 在腾讯云服务器OpenCLoudOS系统…...

【ELK】日志系统部署
一、ELK日志分析系统 1、ELK的组成 ElasticSearchLogStashKibana ELK基于这三个开源日志的收集、存储、检索和可视化的解决方案;可帮助用户快速定位和分析应用程序的故障,监控应用程序性能和安全,以及提供丰富的数据分析和展示功能。 2、完…...

【算法挨揍日记】day08——30. 串联所有单词的子串、76. 最小覆盖子串
30. 串联所有单词的子串 30. 串联所有单词的子串 题目描述: 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如,如果 words ["…...

SpringCloud Gateway--网关服务基本介绍和基本原理
😀前言 本篇博文是关于SpringCloud Gateway的基本介绍,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力…...

使用Vue-cli构建spa项目及结构解析
一,Vue-cli是什么? 是一个官方发布的Vue脚手架工具,用于快速搭建Vue项目结构,提供了现代前端开发所需要的一些基础功能,例如:Webpack打包、ESLint语法检查、单元测试、自动化部署等等。同时,Vu…...

自定义Unity组件——AudioManager(音频管理器)
需求描述 在游戏开发中,音频资源是不可或缺的,通常情况下音频资源随机分布,各个音频的操作和管理都是各自负责,同时对于音频的很多操作逻辑都是大同小异的,这就造成了许多冗余代码的堆叠,除此之外在获取各类…...
leetcode 558 设计内存文件系统
题目 Design an in-memory file system to simulate the following functions: ls: Given a path in string format. If it is a file path, return a list that only contains this files name. If it is a directory path, return the list of file and directory namesin th…...

Haproxy负载均衡群集
HAproxy搭建Web群集一、Web集群调度器1、常见的Web集群调度器2、常用集群调度器的优缺点(LVS ,Nginx,Haproxy)2.1 Nginx2.2 LVS2.3 Haproxy 3、LVS、Nginx、HAproxy的区别 二、Haproxy1、简介2、Haproxy应用分析3、HAProxy的主要特性4、Haproxy调度算法(…...
什么是面包屑导航?
面包屑导航(Breadcrumb Navigation)这个概念来自童话故事“汉赛尔和格莱特”,当汉赛尔和格莱特穿过森林时,不小心迷路了,但是他们发现沿途走过的地方都撒下了面包屑,让这些面包屑来帮助他们找到回家的路。 在网站应用中࿰…...

VS2019创建GIt仓库时剔除文件或目录
假设本地有解决方案“SomeSolution” 1、首先”团队资源管理器“-“创建Git存储库”,选择“仅限本地”、“创建” VS会在解决方案目录下自动生成.gitattributes、.gitignore 2、编辑gitignore,直接拖到VS里或者用记事本打开。添加要剔除的文件或文件夹…...

计算机等级考试—信息安全三级真题六
目录 一、单选题 二、填空题 三、综合题 一、单选题...
vue循环滚动字幕
在Vue.js中创建一个循环滚动字幕的效果通常需要使用一些CSS和JavaScript来实现。以下是一个简单的示例,展示如何使用Vue.js创建一个循环滚动字幕的效果: 首先,在HTML中创建一个Vue实例,并添加一个包含滚动字幕的容器元素ÿ…...

扩展pytest接口自动化框架-MS数据解析功能
【软件测试行业现状】2023年了你还敢学软件测试?未来已寄..测试人该何去何从?【自动化测试、测试开发、性能测试】 开篇 MeterSphere的数据源通过html页面上传后,需要将请求方式进行拆分。 get接口的参数,常以params的方式进行传…...

docker容器安装MongoDB数据库
一:MongoDB数据库 1.1 简介 MongoDB是一个开源、高性能、无模式的文档型数据库,当初的设计就是用于简化开发和方便扩展,是NoSQL数据库产品中的一种。是最 像关系型数据库(MySQL)的非关系型数据库。 它支持的数据结构…...

Python机器学习实战-特征重要性分析方法(3):迭代删除法:Leave-one-out(附源码和实现效果)
实现功能 迭代地每次删除一个特征并评估准确性 实现代码 from sklearn.datasets import load_breast_cancer from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score impo…...

Go的error接口
从本书的开始,我们就已经创建和使用过神秘的预定义error类型,而且没有解释它究竟是什么。实际上它就是interface类型,这个类型有一个返回错误信息的单一方法: type error interface { Error() string } 创建一个error最简单的方…...

RabbitMQ 集群 - 普通集群、镜像集群、仲裁队列
目录 一、RabbitMQ 集群 1.1、前言 1.2、普通集群 1.3、镜像集群 1.4、仲裁队列 一、RabbitMQ 集群 1.1、前言 前面我们已经解决了消息可靠性问题,以及延迟消息问题 和 消息堆积问题. 这最后一章,我们就来解决以下 mq 的可用性 和 并发能力. 1.2、…...

高项新版教程(第四版)解读+学习指导
第四版主要内容 技术部分 信息化教程、软件工程、网络技术是原来的,学习原来的录播。 新基建、工业互联网、车联网、农业现代化、数字化转型、元宇宙等是新增,以直播讲。 管理部分 变化不是太大 。 整合管理、人力变为资源管理、风险管理新增内容。 …...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...

初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
小木的算法日记-多叉树的递归/层序遍历
🌲 从二叉树到森林:一文彻底搞懂多叉树遍历的艺术 🚀 引言 你好,未来的算法大神! 在数据结构的世界里,“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的,它…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
[特殊字符] 手撸 Redis 互斥锁那些坑
📖 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作,想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁,也顺便跟 Redisson 的 RLock 机制对比了下,记录一波,别踩我踩过…...