Linux多线程编程的艺术:封装线程、锁、条件变量和信号量的工程实践
目录
📌这篇博客能带给你什么?
🔥为什么需要封装这些组件?
一、线程封装
框架设计
构造与析构
1.线程创建
2.线程分离
3.线程取消
4.线程等待
二、锁封装
框架设计
构造与析构
1.加锁
2.解锁
3.RAII模式
三、条件变量封装
框架设计
编辑
构造与析构
1.线程等待
2.唤醒一个线程
3.唤醒所有线程
四、信号量的封装
五、源码
📌这篇博客能带给你什么?
-
告别原生API的繁琐:用简洁优雅的C++类封装POSIX线程操作,让多线程开发效率提升50%
-
防泄漏利器:基于RAII机制的智能锁设计,即使发生异常也绝不忘记解锁。
-
开箱即用源码:附带经过压力测试的完整代码库,可直接集成到你的项目。
🔥为什么需要封装这些组件?
当你在开发这些场景时定会深有共鸣:
-
在高并发服务器中管理上百个连接线程时手忙脚乱。
-
因忘记解锁导致死锁,花费数小时定位BUG。
-
不同模块的条件变量使用不一致,引发诡异的内存错误。
-
想快速实现生产者消费者模型,却被信号量接口搞得头晕。
接下来我们直接进入正题:
一、线程封装
框架设计
- 为了更规范和防止命名污染,把线程的封装放在一个命名空间内进行。
- 为区分不同的线程和记录创建线程的个数,我们创建一个静态的int类型来计数。
- 因为线程的本质工作就是执行我们传入的函数,所以需要建立一个function类型方便外部传参。
所以我们做这样的设计:
这里设了模板参数是因为后期如果需要可以把func_t类型该为模板参数T让用户自己设定,为方便讲解后面的内容这里就暂时这么设定。
- fun_t _fun:储存用户传入的函数。
- pthread_t _tid:记录进程号。
- string _name:根据count计数给每个进程起一个名字作为标记。
- bool _isdetch:标记线程是否被分离,因为线程是否被分离决定了某些操作是否能执行,所以需要记录。
- bool _isrun:标记线程是否被创建(运行),同样因为线程是否被创建决定了某些操作是否能执行,所以需要记录。
构造与析构
构造函数主要是对成员变量初始化,要注意这里需要对count做++。
thread(func_t fun): _fun(fun), _tid(0), _isdetach(false), _isrun(false)
{_name = "thread-" + to_string(count);count++;
}
~thread(){}
1.线程创建
pthread_create的使用
pthread_create声明如下:
#include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
- 参数thead:输出型参数,需要传入一个pthread_t类型的变量的地址,可以得到线程的tid。
- 参数attr:这个参数跟属性相关的设定,几乎用不到,这里也就不再进,设为nullptr就行。
- 参数start_routine:传入一个返回类型为void*,参数为void*的自定义函数,线程创建后会执行这个函数。
- 参数arg:传入一个void*类型的参数,最后会被作为start_routine的参数。
- 返回值:
- 非0:创建失败。
- 0:创建成功
pthread_create函数要求传入一个void *(*start_routine) (void *)类型的函数指针,但为了可以让用户更灵活的使用,我们可以在这里进行一下封装。也就是说希望用户想传什么类型的函数指针都行,那么可以单独设计这样一个函数,如下:
static void *Func(void *p)
{thread<T> *self = (thread<T> *)p;self->_isrun = true;self->_fun();return nullptr;
}
这样在这个函数内再调用用户传入的函数_fun就行了。一个很巧妙的设计。
需要注意的几点:
- 普通的成员函数的参数是带有this指针的,不满足start_routine( pthread_create函数的参数)的要求,所以我们把它设为了静态成员函数(即加static关键字)。这样就不会有this指针。
- 没有了this指针就找不到成员变量,所以在给Func传参时需要传this指针。
- 最后不要把Func函数暴露给用户,把它设为私有(private)更为合理。
Start实现如下:
bool Start()
{if (_isrun)return false;int n = pthread_create(&_tid, nullptr, &Func, (void *)this);if (n != 0){perror("pthread_create");return false;}elsereturn true;
}
2.线程分离
如果主线程不想再关心新线程的任务执行情况等,那么就可以设置新线程为分离状态,分离的新线程依旧在进程地址空间中,依旧与主线程共享资源,线程被分离后不用进join等待,join会失败。
pthread_detach的使用
pthread_detach声明如下:
int pthread_detach(pthread_t thread);
- 参数pthread_t thread:参入需要分离的线程tid。
Detach的实现:
bool Detach()
{if (_isdetach) return false;if (_isrun) pthread_detach(_tid);_isdetach = true;
}
3.线程取消
线程被取消也就是线程退出,退出结果是-1。
pthread_cancel的使用
pthread_cancel声明:
int pthread_cancel(pthread_t thread);
- 参数thread:传入需要取消的线程tid。
Cancel的实现:
bool Cancel()
{if (!_isrun) return false;pthread_cancel(_tid); return true;
}
4.线程等待
线程等待和进程等待类似,为了获取到任务的完成情况。如果线程不进行等待就会造成内存泄漏。
pthread_join声明:
int pthread_join(pthread_t thread, void **retval);
- 参数thread:传入需要等待的线程tid。
- 参数retval:输出型参数,传入void*类型的地址,得到线程的任务完成情况,这个retval得到的也就是在我们传入自定义函数中返回的void*类型。
- 返回值
- 非0:失败。
- 0:成功。
Join实现:
void *Join()
{void *ret = nullptr;pthread_join(_tid, &ret);return ret;
}
二、锁封装
在 Linux 中,锁是一种互斥机制,用于控制多线程或多进程对共享资源的访问,避免竞态条件(Race Condition)和数据不一致问题。锁功能的介绍:
-
功能:确保同一时间只有一个线程能访问共享资源(临界资源)。
-
特点:
-
线程独占,其他线程会被阻塞直到锁释放。
-
适用于线程间的互斥。
-
锁的核心作用
-
原子性:确保操作不可分割,即在CPU中一次性完成,在此期间可以被切换,但不会被其它执行流介入。
-
可见性:保证锁释放后修改对其他线程可见。
-
有序性:防止指令重排导致的逻辑错误。
框架设计
- Mutex:完成锁的初始化
- void Lock:加锁。
- void Unlock:解锁。
- pthread_mutex_t* GetLock():得到锁的信息,方便与后面的条件变量、信号量进行交互。
- ~Mutex:锁的释放。
- pthread_mutex_t _mutex:储存锁的信息。
构造与析构
构造需要完成锁的初始化,使用pthread_mutex_init函数。
pthread_mutex_init的声明:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
- 参数restrict mutex:传入一个pthread_mutex_t类型的变量地址,函数会初始化这个互斥锁。
- 参数restrict attr:用来设置锁的属性,这里不做探讨,设为nullptr即可。
Mutex()实现:
Mutex()
{pthread_mutex_init(&_mutex, nullptr);
}
析构函数完成锁的释放
锁本身不会直接导致内存泄漏,但如果使用不当,可能会间接引发资源泄漏或死锁问题,所以我们需对锁进行destroy。
pthread_destroy的声明:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
- 参数mutex:传入一个pthread_mutex_t类型的变量地址,函数会回收这个互斥锁。
~Mutex()实现
~Mutex()
{pthread_mutex_destroy(&_mutex);
}
1.加锁
pthread_mutex_lock的使用
pthread_mutex_lock的声明
int pthread_mutex_lock(pthread_mutex_t *mutex);
- pthread_mutex_t:传入锁的地址,类型为pthread_mutex_t*。
void Lock()实现:
void Lock()
{pthread_mutex_lock(&_mutex);
}
2.解锁
pthread_mutex_unlock的使用
pthread_mutex_lock的声明
int pthread_mutex_unlock(pthread_mutex_t *mutex);
- pthread_mutex_t:传入锁的地址,类型为pthread_mutex_t*。
void Unlock()实现:
void Unlock()
{pthread_mutex_unlock(&_mutex);
}
3.RAII模式
class LockGuard
{
public:LockGuard(Mutex& lock): _lock(lock){_lock.Lock();}~LockGuard(){_lock.Unlock();}
private:Mutex& _lock;
};
RAII模式的锁好处在于在创建对象时自动加锁,在销毁时自动解锁,是全自动的,当临界区代码抛出异常时,RAII 对象的析构函数仍会执行,确保锁被释放,避免死锁。
但是在使用RAII模式的锁时要注意作用域的问题,确保锁在正确的作用域添加和释放。
三、条件变量封装
条件变量是用来使线程或进程之间同步的,避免了一个锁被同一执行流循环式的重复占用。
- 锁:解决互斥问题(对错问题)
- 条件变量:解决同步问题(是否公平问题)
它是怎么实现同步呢?其实是执行流之间互相“通知”,比如当某个条件不满足的时候,这个执行流就不要申请锁了,就在这里等着。等条件满足其它执行流会通知你。
框架设计
- Cond:完成条件变量的初始化。
- void Wait:执行流进行等待。
- void Signal:唤醒一个执行流。
- void Broadcast:唤醒所有执行流。
- ~Cond: 销毁条件变量。
构造与析构
条件变量的接口使用和锁的接口完全类似。
pthread_cond_init声明
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
- 参数restrict cond:传入一个pthread_cond_t类型的变量地址,函数会初始化这个条件变量。
- 参数restrict attr:用来设置条件变量的属性,这里不做探讨,设为nullptr即可。
pthread_cond_destroy声明
int pthread_cond_destroy(pthread_cond_t *cond);
- 参数mutex:传入一个pthread_cond_t类型的变量地址,函数会回收这个条件变量。
Cond与~Cond实现:
Cond()
{pthread_cond_init(&_cond,nullptr);
}
~Cond()
{pthread_cond_signal(&_cond);
}
1.线程等待
pthread_cond_wait的声明
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
- 参数restrict cond:传入条件变量地址。
- 参数restrict mutex:传入一个互斥锁。
pthread_cond_wait为什么要传入锁呢?其实是这样的,首先执行流申请了锁进入临界区后却因为某些条件不满足,无法往下执行,所以需要等待,但又不能一直霸占着锁,要不然就变成死锁了,所以需要把锁释放。
pthread_cond_wait函数的执行逻辑就是释放锁然后等待,当被唤醒时再次申请锁。
void Wait(Mutex mutex) 实现
void Wait(Mutex mutex)
{pthread_cond_wait(&_cond,mutex.GetLock());
}
2.唤醒一个线程
pthread_cond_signal作用是唤醒一个正在等待的线程,声明如下:
int pthread_cond_signal(pthread_cond_t *cond);
void Signal()实现
void Signal()
{pthread_cond_signal(&_cond);
}
3.唤醒所有线程
pthread_cond_broadcast作用是唤醒所有正在等待的线程,声明如下:
void Broadcast()实现
void Broadcast()
{pthread_cond_broadcast(&_cond);
}
四、信号量的封装
信号量的理解
当一个共享资源可以分为小块去访问,那么就可以使用一个计数器去记录这些小块资源,在被使用中的有多少未被使用的有多少,它相当于一种资源的预定机制。比如在生产者消费者模型中用环形队列做交易场所,那么信号量就可以来完成生产者和消费者之间需要的互斥和同步功能。
- P操作:资源预定成功,信号量减1。
- V操作:资源使用结束,信号量加1。
Sem(信号量)的封装几乎和Mutex和Cond一样,这里就不做详细讲解。
#include <semaphore.h>
#define DEF 1
class Sem
{
public:Sem(int val = DEF){sem_init(&_sem,0,val);}void P(){sem_wait(&_sem);}void V(){sem_post(&_sem);}~Sem(){sem_destroy(&_sem);}
private:sem_t _sem;
};
五、源码
1.Thread.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <functional>
#include <string>
#include <errno.h>
using namespace std;
namespace my_thread{static int count = 1;template <typename T>class thread{using func_t = std::function<void()>;private:static void *Func(void *p){thread<T> *self = (thread<T> *)p;self->_isrun = true;self->_fun();return nullptr;}public:thread(func_t fun): _fun(fun), _tid(0), _isdetach(false), _isrun(false){_name = "thread-" + to_string(count);count++;}bool Start(){if (_isrun)return false;int n = pthread_create(&_tid, nullptr, &Func, (void *)this);if (n != 0){perror("pthread_create");return false;}elsereturn true;}bool Cancel(){if(_isrun)pthread_cancel(_tid);else return false;return true;}bool Detach(){if (_isdetach)return false;if(_isrun)pthread_detach(_tid);_isdetach = true;}void *Join(){void *ret = nullptr;pthread_join(_tid, &ret);return ret;}~thread(){}private:func_t _fun;pthread_t _tid;string _name;bool _isdetach;bool _isrun;};}
2.Mutex.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <string>
#include <unistd.h>
using namespace std;
namespace my_mutex
{class Mutex{public:Mutex(){pthread_mutex_init(&_mutex, nullptr);}void Lock(){pthread_mutex_lock(&_mutex);}void Unlock(){pthread_mutex_unlock(&_mutex);}pthread_mutex_t* GetLock(){return &_mutex;}~Mutex(){pthread_mutex_destroy(&_mutex);}private:pthread_mutex_t _mutex;};class LockGuard{public:LockGuard(Mutex& lock): _lock(lock){_lock.Lock();}~LockGuard(){_lock.Unlock();}private:Mutex& _lock;};
}
3.Cond.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include "mutex.hpp"
using namespace my_mutex;
namespace my_cond
{class Cond{public:Cond(){pthread_cond_init(&_cond,nullptr);}void Wait(Mutex mutex){pthread_cond_wait(&_cond,mutex.GetLock());}void Signal(){pthread_cond_signal(&_cond);}void Broadcast(){pthread_cond_broadcast(&_cond);}~Cond(){pthread_cond_signal(&_cond);}private:pthread_cond_t _cond;};
}class Cond
{
public:Cond();void Wait(Mutex mutex);void Signal();void Broadcast();~Cond();
private:pthread_cond_t _cond;
};
4. Sem.hpp
#pragma once
#include <semaphore.h>
#define DEF 1
class Sem
{
public:Sem(int val = DEF){sem_init(&_sem,0,val);}void P(){sem_wait(&_sem);}void V(){sem_post(&_sem);}~Sem(){sem_destroy(&_sem);}
private:sem_t _sem;
};
相关文章:
Linux多线程编程的艺术:封装线程、锁、条件变量和信号量的工程实践
目录 📌这篇博客能带给你什么? 🔥为什么需要封装这些组件? 一、线程封装 框架设计 构造与析构 1.线程创建 2.线程分离 3.线程取消 4.线程等待 二、锁封装 框架设计 构造与析构 1.加锁 2.解锁 3.RAII模式 三、条件…...
2025年智慧能源与控制工程国际学术会议(SECE 2025)
官网:www.ic-sece.com 简介 2025年智慧能源与控制工程国际学术会议(SECE 2025)将于2025年4月18日线上会议形式召开,这是一个集中探讨全球智慧能源和控制工程领域创新和挑战的国际学术平台。旨在汇集全球领域内的学者、研究人员、…...
Android 16开发实战指南|锁屏交互+Vulkan优化全解析
一、环境搭建与项目初始化 1. 安装Android Studio Ladybug 下载地址:Android Studio官网关键配置: # 安装后立即更新SDK SDK Manager → SDK Platforms → 安装Android 16 (Preview) SDK Manager → SDK Tools → 更新Android SDK Build-Tools至34.0.0 # 通过命令行安装SDK组…...
sscanf() 用法详解
sscanf() 是 scanf() 的变体,它用于从字符串中提取格式化数据,常用于解析输入字符串。 1️⃣ sscanf() 语法 int sscanf(const char *str, const char *format, ...); str:要解析的字符串(必须是 const char*,可以用…...
0基础入门scrapy 框架,获取豆瓣top250存入mysql
一、基础教程 创建项目命令 scrapy startproject mySpider --项目名称 创建爬虫文件 scrapy genspider itcast "itcast.cn" --自动生成 itcast.py 文件 爬虫名称 爬虫网址 运行爬虫 scrapy crawl baidu(爬虫名) 使用终端运行太麻烦了,而且…...
Linux常见操作命令(2)
(一)复制和移动 复制和移动都分为文件和文件夹,具体的命令是cp和mv。 1.复制文件(复制的文件要是已创建) 格式:cp 源文件 目标文件。 示例:把filel.txt复制一份得到file2.txt。 那么对应的命令就是&#x…...
谷歌将 Android OS 完全转变为 “内部开发”
2025 年 3 月 27 日,据 Android Authority 报道,谷歌证实将从下周开始完全在内部分支机构闭门开发安卓操作系统。相关信息如下: 背景:多年来,谷歌同时维护着两大安卓主要分支,一是面向公众开放的 “安卓开源…...
移动端六大语言速记:第2部分 - 控制结构
移动端六大语言速记:第2部分 - 控制结构 本文继续对比Java、Kotlin、Flutter(Dart)、Python、ArkTS和Swift这六种移动端开发语言的控制结构,帮助开发者快速掌握各语言的语法差异。 2. 控制结构 2.1 条件语句 各语言条件语句的语法对比: …...
【 Vue 2 中的 Mixins 模式】
Vue 2 中的 Mixins 模式 在 Vue 2 里,mixins 是一种灵活的复用代码的方式,它能让你在多个组件间共享代码。借助 mixins,你可以把一些通用的选项(像 data、methods、computed 等)封装到一个对象里,然后在多…...
STM32F103_LL库+寄存器学习笔记13 - 梳理外设CAN与如何发送CAN报文(串行发送)
导言 CAN总线因其高速稳定的数据传输与卓越抗干扰性能,在汽车、机器人及工业自动化中被广泛应用。它采用分布式网络结构,实现多节点间实时通信,确保各控制模块精准协同。在汽车领域,CAN总线连接发动机、制动、车身系统,…...
DataPlatter:利用最少成本数据提升机器人操控的泛化能力
25年3月来自中科院计算所的论文“DataPlatter: Boosting Robotic Manipulation Generalization with Minimal Costly Data”。 视觉-语言-动作 (VLA) 模型在具身人工智能中的应用日益广泛,这加剧对多样化操作演示的需求。然而,数据收集的高成本往往导致…...
受控组件和非受控组件的区别
在 React 中,受控组件(Controlled Components) 和 非受控组件(Uncontrolled Components) 是处理表单元素的两种不同方式,它们的核心区别在于 数据管理的方式 和 与 React 的交互模式。 受控组件…...
Mhand Pro 多节点动作捕捉手套:一副手套多场景应用
随着动作捕捉技术的发展,动捕手套的出现为虚拟现实交互、VR游戏开发、机器臂/灵巧手遥操作等方面带来了更加便捷可行的方案。 广州虚拟动力作为一家在动作捕捉领域深耕多年的公司,基于传感器技术而研发的多节点惯性动作捕捉手套,兼具VR交互与…...
Kafka消息丢失全解析!原因、预防与解决方案
作为一名高并发系统开发工程师,在使用消息中间件的过程中,无法避免遇到系统中消息丢失的问题,而Kafka作为主流的消息队列系统,消息丢失问题尤为常见。 在这篇文章中,将深入浅出地分析Kafka消息丢失的各种情况…...
BERT与Transformer到底选哪个-上部
一、先理清「技术家谱」:BERT和Transformer是啥关系? 就像「包子」和「面食」的关系——BERT是「Transformer家族」的「明星成员」,而GPT、Qwen、DeepSeek这些大模型则是「Transformer家族」的「超级后辈」。 1.1 BERT:Transfor…...
【Unity】记录TMPro使用过程踩的一些坑
1、打包到webgl无法输入中文,编辑器模式可以,但是webgl不行,试过网上的脚本,还是不行 解决方法:暂时没找到 2、针对字体asset是中文时,overflow的效果模式处理奇怪,它会出现除了overflow模式以…...
数据处理的两种范式:深入解析OLTP与OLAP系统
目录 前言1. OLTP:业务运作的基石1.1 OLTP的核心定义与价值1.2 OLTP的技术架构特点1.3 OLTP的典型应用场景 2. OLAP:决策支持的大脑2.1 OLAP的基本概念与作用2.2 OLAP的技术实现方式2.3 OLAP的应用实践 3. OLTP与OLAP的对比与融合3.1 核心差异的深度解析…...
本地飞牛NAS快速部署WordPress个人网站并一键上线公网远程访问
文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗…...
windows环境下的cmake使用
创建一个目录testcmake 进入目录 创建一个文件main.cpp : #include <iostream> using namespace std; int main(){cout<<"what is going on?"<<endl;return 0; }再创建一个cmakelists.txt set(CMAKE_CXX_STANDARD 20) add_executable(test2 mai…...
多线程(多线程案例)(续~)
目录 一、单例模式 1. 饿汉模式 2. 懒汉模式 二、阻塞队列 1. 阻塞队列是什么 2. 生产者消费者模型 3. 标准库中的阻塞队列 4. 自实现阻塞队列 三、定时器 1. 定时器是什么 2. 标准库中的定时器 欢迎观看我滴上一篇关于 多线程的博客呀,直达地址…...
同步SVPWM调制策略的初步学习记录
最近项目需要用到一些同步调制SVPWM相关的内容(现在的我基本都是项目驱动了),因此对该内容进行一定的学习。 1 同步SVPWM调制的背景 我们熟知的一些知识是:SVPWM(空间矢量脉宽调制)是一种用于逆变器的调制…...
权重参数矩阵
目录 1. 权重参数矩阵的定义与作用 2. 权重矩阵的初始化与训练 3. 权重矩阵的解读与分析 (1) 可视化权重分布 (2) 统计指标分析 4. 权重矩阵的常见问题与优化 (1) 过拟合与欠拟合 (2) 梯度问题 (3) 权重对称性问题 5. 实际应用示例 案例1:全连接网络中的…...
堆叠虚拟化
各厂商叫法不同:思科 VSS 锐捷 VSU 华为 Stack 华三 IRF 虚拟化为一台设备进行管理,从而实现高可靠性。当任意交换机故障时,都能实现设备、链路切换,保护客户业务稳定运行 传统的园区网高可靠性技术出现故障时切换时间很难做到毫…...
3.31-4 性能面试题
面试题 1、性能问题的六个特征: (1)、持续缓慢: (2)、随着时间推进越来越慢、 (3)、随着负载增加越来越慢、 (4)、零星挂起或异常错误、 (5…...
2025年最新自动化/控制保研夏令营预推免面试真题分享(东南/浙大/华科清华)
笔者来2021级本科自动化专业,以下部分将介绍我在夏令营以及预推免期间发生经历和问题 东南大学自动化学院 东南大学: 东南大学面试有一个十分明显的特点,就是极其注重专业课,基本上就是在面试的时候电脑上会有几个文件夹&#x…...
freecad手动装插件 add on
python工作台输入 FreeCAD.ConfigGet("UserAppData") 在返回的地址上新建文件夹:Mod #like /home/chen/snap/freecad/common 进入Mod #like /home/chen/snap/freecad/common/Mod git clone 你要的项目 #like git clone https://github.com/looooo/f…...
mysql 主从搭建步骤
主库: 开启log-bin参数,log-bin 参数修改需要重启服务器 --You can change the server_id value dynamically by issuing a statement like this:SET GLOBAL server_id 2;--to enable binary logging using a log file name prefix of mysql-bin, and c…...
从AI大模型到MCP中台:构建下一代智能服务的核心架构
从AI大模型到MCP中台:构建下一代智能服务的核心架构 引言:AI大模型带来的服务重构革命 在ChatGPT掀起全球AI热潮的今天,大模型展现出的惊人能力正在重塑整个软件服务架构。但鲜为人知的是,真正决定AI服务成败的不仅是模型本身&a…...
31天Python入门——第18天:面向对象三大特性·封装继承多态
你好,我是安然无虞。 文章目录 面向对象三大特性1. 封装2. 继承3. 多态4. 抽象基类5. 补充练习 面向对象三大特性 面向对象编程(Object-Oriented Programming, 简称OOP)有三大特性, 分别是封装、继承和多态.这些特性是面向对象编程的基础, …...
css_z-index属性
z-index 工作原理及层叠上下文(Stacking Context) 在 CSS 中,z-index 主要用于控制元素的堆叠顺序,决定哪些元素显示在上层,哪些元素在下层。它的工作原理涉及 层叠上下文(Stacking Context)&a…...
ros2--xacro
什么是xacro 在ROS 2中,Xacro(XML Macros)是一种基于XML的宏语言,专门用于简化URDF(Unified Robot Description Format)文件的编写。它通过宏定义、变量替换和代码复用等功能,让机器人模型的描…...
Nordic 新一代无线 SoC nRF54L系列介绍
目录 概述 1 nRF54L系列特点 1.1 内存 1.2 芯片封装 2 增强的多协议支持 3 其他特性 4 nRF54L系列MCU特性 4.1 多协议无线电 4.2 安全性 4.3 存储空间 4.4 工作参数 4.5 调试接口 4.6 外设 概述 全新 nRF54L 系列的所有三款器件均将 2.4 GHz 无线电和 MCU 功能 (包括…...
力扣HOT100之矩阵:240. 搜索二维矩阵 II
这道题直接暴力AC的,虽然也能过,但是耗时太长了。 class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int edge min(matrix.size(), matrix[0].size()) - 1; //先在edge * edge的矩阵中搜索for…...
一个判断A股交易状态的python脚本
最近在做股票数据相关的项目,需要用到判断某一天某个时刻A股的状态,比如休市,收盘,交易中等,发动脑筋想了一下,这个其实还是比较简单的,这里我把实现方法分享给大家。 思路 当天是否休市 对于某…...
为什么package.json里的npm和npm -v版本不一致?
这个情况出现是因为package.json里的 npm 版本和系统实际使用的 npm 版本是两个不同的概念。让我来解释一下: 原因解释 全局 npm vs 项目依赖: npm -v显示的是系统全局安装的 npm 版本(位于/usr/bin/npm或类似路径)package.jso…...
Rust 有问有答之 use 关键字
use 是什么# use 是 Rust 编程语言的关键字。using 是 编程语言 C# 的关键字。 关键字是预定义的保留标识符,对编译器有特殊意义。 using 关键字有三个主要用途: using 语句定义一个范围,在此范围的末尾将释放对象。 using 指令为命名空间创…...
[skip]CBAM
论文题目:CBAM: Convolutional Block Attention Module 中文题目:CBAM: 注意力卷积模块 0摘要 我们提出了卷积块注意力模块(CBAM),一个简单而有效的前馈卷积神经网络注意力模块。给定一个中间特征图,我们的模块沿着两个独立的维度(通道和空间)顺序推断注意力图,然后…...
突破反爬困境:SDK开发,浏览器模块(七)
声明 本文所讨论的内容及技术均纯属学术交流与技术研究目的,旨在探讨和总结互联网数据流动、前后端技术架构及安全防御中的技术演进。文中提及的各类技术手段和策略均仅供技术人员在合法与合规的前提下进行研究、学习与防御测试之用。 作者不支持亦不鼓励任何未经授…...
在MFC中使用Qt(四):使用属性表(Property Sheet)实现自动化Qt编译流程
前言 首先回顾下前面文章介绍的: 在MFC中使用Qt(一):玩腻了MFC,试试在MFC中使用Qt!(手动配置编译Qt) 在MFC中使用Qt(二):实现Qt文件的自动编译流…...
相机镜头景深
文章目录 定义影响因素实际应用特殊情况 参考:B站优致谱视觉 定义 景深是指在摄影机镜头或其他成像器前沿着能够取得清晰图像的成像器轴线所测定的物体距离范围。简单来说,就是在一张照片中,从前景到背景,能够保持清晰锐利的区域…...
HTML实现图片上添加水印的工具
HTML实现图片上添加水印的工具 本文介绍两种实现方式:图片上添加文字水印和图片上添加图片水印。部分源码参照自网络。 一、图片上添加文字水印 先看效果图: 源码如下: <!DOCTYPE html> <html lang"zh"> <head&…...
mysql JSON_ARRAYAGG联合JSON_OBJECT使用
父表数据(表名:class) idname1一年级2二年级3三年级 子表数据(表名:students) idnameclassId11张三112李四113小明3 sql查询(推荐使用方法一) 方法一 (使用IFNull判断子表数据是否…...
VMware虚拟机 ubuntu22.04无法与共享粘贴板和拖拽文件的解决方案
VMware虚拟机 ubuntu22.04无法与共享粘贴板和拖拉文件的解决方案 卸载VMware tools安装open-vm-tools还无法拖拽文件 卸载VMware tools 确保卸载完vmware-tools # 进入vmware-tools安装目录/bin sudo vmware-uninstall-tools.pl sudo rm -rf /usr/lib/vmware-tools sudo apt-…...
C++STL---<functional>
C标准库中的 <functional> 库是一个强大的工具集,它提供了用于处理函数对象、函数绑定、函数包装等功能的设施,极大地增强了代码的灵活性和可复用性。 1. 函数对象(Functors) 函数对象,也被称作仿函数…...
【Android】BluetoothSocket.connect () 的实现与协议栈交互源码解析
本文以 Android 蓝牙框架中的BluetoothSocket.connect()方法为切入点,深入剖析 Android 设备与远程蓝牙设备建立连接的全流程。从 Java 层的 API 调用出发,逐步追踪至 JNI 层的接口转发,最终进入 Buedroid 协议栈(RFCOMM/L2CAP 层),揭示蓝牙连接的核心机制。重点解析了权…...
算法导论(动态规划)——简单多状态
算法思路(17.16) 状态表示: 在处理线性动态规划问题时,我们可以通过“经验 题目要求”来定义状态表示。通常有两种选择: 以某个位置为结尾的情况;以某个位置为起点的情况。 本题中,我们选择更常…...
主成分分析(PCA)学习介绍及其高阶应用,金融风险分析
前言 主成分分析(Principal Component Analysis, PCA)是统计学中一种重要的降维技术。它通过寻找数据中各特征之间的线性关系,来降低数据的维度,同时保留数据中的主要信息。PCA在机器学习、信号处理、图像处理等领域广泛应用&…...
利用 SSRF 和 Redis 未授权访问进行内网渗透
目录 环境搭建 编辑 发现内网存活主机 编辑 扫描内网端口 编辑 利用 Redis 未授权访问进行 Webshell 写入 步骤1:生成 payload 方式1:使用python生成 payload 方式二:使用 Gopher 工具 步骤 2:写入 Webshell…...
计算机网络和因特网
目录 1、什么是Internet? 1.1定义 1.2具体构成描述 2、什么是协议? 2.1 服务描述 2.2 网络协议 3、网络边缘 3.1 定义与组成 3.2 模式 3.3服务 4、接入网、物理媒介 4.1、宽带有线接入网技术 4.2、宽带无线接入网技术 5、网络核心…...
1.oracle修改配置文件
1.找到oracle的安装路径 D:\app\baozi\product\11.2.0\dbhome_1\NETWORK\ADMIN ,修改下面的两个文件。如果提示没有权限,可以先把这两个文件复制到桌面,修改完后,在复制回来。 2.查看自己电脑的主机名, 右击 - 此电脑 …...