【Linux】条件变量封装类及环形队列的实现
📢博客主页:https://blog.csdn.net/2301_779549673
📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨


文章目录
- 📢前言
- 🏳️🌈一、条件变量 Cond
- 1.1 条件变量的本质
- 1.2 核心作用
- 🏳️🌈二、条件变量封装类
- 2.1 类定义
- 2.2 构造函数
- 2.3 Wait 方法
- 2.4 Notify 和 NotifyAll
- 2.5 析构函数
- 2.6 整体代码
- 2.7 示例用法
- 🏳️🌈三、POSIX 信号量
- 3.1 Sem类核心功能
- 3.2 设计意义
- 3.3 Sem类封装
- 🏳️🌈四、环形队列的逻辑及实现
- 4.1 核心设计
- 4.2 成员变量
- 4.3 核心接口
- 4.3.1 构造函数
- 4.3.2Equeue(生产者接口)
- 4.3.3 Pop(消费者接口)
- 4.4 同步机制
- 4.4.1 信号量控制
- 4.4.2 互斥锁保护
- 4.5 整体代码
- 4.6 测试代码
- 👥总结
📢前言
紧接上回 BlockQueue生产消费模型 的实现,这篇文章,笔者来介绍一下 条件变量 的意义和作用,然后进行 封装,并实现 BlockQueue 的加强版 环形阻塞队列 RingBuffer
🏳️🌈一、条件变量 Cond
1.1 条件变量的本质
条件变量 是 线程间的通信机制,用于在 特定条件不满足时挂起线程,并在条件满足时 唤醒线程继续执行。它必须与 **互斥锁(Mutex)** 配合使用,确保操作的原子性。
1.2 核心作用
-
避免忙等待(Busy Waiting)
问题:没有条件变量时,线程需反复检查条件是否满足(while(条件不满足)),浪费CPU资源。
解决:条件变量让线程在条件不满足时主动休眠,直到被其他线程唤醒。 -
实现线程间协作
生产者-消费者模型:生产者通知消费者“数据已准备好”,消费者通知生产者“缓冲区有空位”。
任务队列:工作线程在队列为空时休眠,主线程添加任务后唤醒工作线程。 -
保证操作的原子性
通过 互斥锁 + 条件变量的组合,确保线程在检查条件和进入等待状态的整个过程是原子的,避免竞态条件。
🏳️🌈二、条件变量封装类
头文件和命名空间:
- 包含 和 <pthread.h>,后者提供 POSIX 线程操作。
- 使用
LockModule命名空间中的Mutex类,表示互斥锁。
2.1 类定义
class Cond {
public:Cond();void Wait(Mutex &mutex);void Notify();void NotifyAll();~Cond();
private:pthread_cond_t _cond;
};
封装了 pthread_cond_t,提供初始化、等待、通知和销毁功能
2.2 构造函数
Cond() {int n = ::pthread_cond_init(&_cond, nullptr);(void)n; // 忽略返回值
}
- 调用
pthread_cond_init初始化条件变量。 - 返回值
n被忽略,实际应用中应检查错误(如返回非零表示失败)。
2.3 Wait 方法
void Wait(Mutex &mutex) {int n = ::pthread_cond_wait(&_cond, mutex.LockPtr());
}
作用:使当前线程等待条件变量,并释放关联的互斥锁。
参数:Mutex 对象的引用,调用其 LockPtr() 获取底层 pthread_mutex_t*。
流程:
- 调用线程必须已锁定
mutex。 pthread_cond_wait自动释放mutex并阻塞,直到被唤醒。- 被唤醒后重新获取
mutex,继续执行。
2.4 Notify 和 NotifyAll
void Notify() {::pthread_cond_signal(&_cond); // 唤醒一个等待线程
}
void NotifyAll() {::pthread_cond_broadcast(&_cond); // 唤醒所有等待线程
}
区别:
Notify()唤醒至少一个等待线程。NotifyAll()唤醒所有等待线程。- 应在持有相同互斥锁时调用,以避免竞态条件
2.5 析构函数
~Cond() {::pthread_cond_destroy(&_cond); // 销毁条件变量
}
确保没有线程等待时销毁,否则行为未定义
2.6 整体代码
Mutex.hpp
#pragma once
#include <iostream>
#include <pthread.h> // POSIX线程库头文件namespace LockModule
{// 互斥锁封装类(不可拷贝构造/赋值)class Mutex{public:// 禁止拷贝(保护系统锁资源)Mutex(const Mutex&) = delete;const Mutex& operator = (const Mutex&) = delete;// 构造函数:初始化POSIX互斥锁Mutex(){// 初始化互斥锁属性为默认值int n = ::pthread_mutex_init(&_lock, nullptr);(void)n; // 实际开发建议处理错误码}// 析构函数:销毁锁资源~Mutex(){// 确保锁已处于未锁定状态int n = ::pthread_mutex_destroy(&_lock);(void)n; // 生产环境应检查返回值}// 加锁操作(阻塞直至获取锁)void Lock(){// 可能返回EDEADLK(死锁检测)等错误码int n = ::pthread_mutex_lock(&_lock);(void)n; // 简化处理,实际建议抛异常或记录日志}// 解锁操作(必须由锁持有者调用)void Unlock(){// 未持有锁时解锁将返回EPERMint n = ::pthread_mutex_unlock(&_lock);(void)n; }// 获取底层锁指针(可用于自定义条件变量)pthread_mutex_t *LockPtr(){return &_lock;}private:pthread_mutex_t _lock; // 底层锁对象};// RAII锁守卫(自动管理锁生命周期)class LockGuard{public:// 构造时加锁(必须传入已初始化的Mutex引用)LockGuard(Mutex &mtx):_mtx(mtx){_mtx.Lock(); // 进入临界区}// 析构时自动解锁(异常安全保证)~LockGuard(){_mtx.Unlock(); // 离开作用域自动释放}private:Mutex &_mtx; // 引用方式持有,避免拷贝导致未定义行为};
}
Cond.hpp
#pragma once#include <iostream>
#include <pthread.h>#include "Mutex.hpp"namespace CondModule{using namespace LockModule;class Cond{public:Cond(){int n = ::pthread_cond_init(&_cond, nullptr);(void)n;}void Wait(Mutex& mutex){// pthread_mutex_lock() 的作用是 锁定互斥锁,直到成功为止。// 第一个参数 cond 是一个条件变量的标识符,第二个参数 mutex 是一个互斥锁的标识符。// pthread_cond_wait() 用来等待条件变量 cond 被 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。// 它将阻塞调用线程,直到被唤醒或被中断。// 返回值是 0 表示成功,其他值表示出错。// 出错时,errno 被设置。// 成功时,调用线程获得互斥锁 mutex,直到被 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒。int n = ::pthread_cond_wait(&_cond, mutex.LockPtr());(void)n;}void Notify(){// pthread_cond_signal() 用来唤醒一个正在等待条件变量的线程。// 第一个参数 cond 是一个条件变量的标识符。// 返回值是 0 表示成功,其他值表示出错。// 出错时,errno 被设置。// 成功时,一个正在等待条件变量的线程被唤醒。int n = ::pthread_cond_signal(&_cond);(void)n;}void NotifyAll(){// pthread_cond_broadcast() 用来唤醒所有正在等待条件变量的线程。// 第一个参数 cond 是一个条件变量的标识符。// 返回值是 0 表示成功,其他值表示出错。// 出错时,errno 被设置。// 成功时,所有正在等待条件变量的线程被唤醒。int n = ::pthread_cond_broadcast(&_cond);(void)n;}~Cond(){int n = ::pthread_cond_destroy(&_cond);(void)n;}private:pthread_cond_t _cond;};
}
2.7 示例用法
Mutex mutex;
Cond cond;
std::queue<int> buffer;// 生产者线程
void producer() {mutex.Lock();while (buffer.full()) {cond.Wait(mutex); // 等待缓冲区非满}buffer.push(1);cond.Notify(); // 通知消费者mutex.Unlock();
}// 消费者线程
void consumer() {mutex.Lock();while (buffer.empty()) {cond.Wait(mutex); // 等待缓冲区非空}buffer.pop();cond.Notify(); // 通知生产者mutex.Unlock();
}
🏳️🌈三、POSIX 信号量
POSIX信号量 和 SystemV信号量 作用相同,都是用于同步操作,达到无冲突的访问共享资源目的。但POSIX可以用于线程间同步。
因此我们可以封装一个 Sem类 来统筹管理 POSIX信号量
扮演 线程同步协调者 的角色,通过 信号量机制 精准控制生产与消费的节奏,确保缓冲区的线程安全与高效运作。其设计不仅简化了复杂的同步逻辑,还通过 封装 和 *RAII机制 *提升了代码的可靠性和可维护性,是多线程编程中资源同步的典范实现。
3.1 Sem类核心功能
用于简化 POSIX 信号量的使用。其主要功能如下:
- 初始化:创建并初始化信号量。
- P 操作(P()):申请资源(信号量减 1),若资源不足则阻塞。
- V 操作(V()):释放资源(信号量加 1),唤醒等待线程。
- 销毁:清理信号量资源。
3.2 设计意义
- 简化信号量操作
- 封装底层 API:将
sem_init、sem_wait、sem_post、sem_destroy封装为类方法,隐藏实现细节。 - 统一接口:通过
P()和V()提供直观的语义(“申请”和“释放”)。
- 提高代码可维护性
RAII 机制:构造函数初始化资源,析构函数自动释放,避免资源泄漏。
{Sem sem(5); // 信号量初始化sem.P(); // 使用信号量
} // 作用域结束自动调用 ~Sem()
- 支持多种同步场景
通过调整初始值value,可适配不同场景:
- 互斥锁(Mutex):value = 1(二元信号量)。
- 资源池:value = N(表示最多允许 N 个线程同时访问资源)。
- 生产者-消费者模型:通过两个信号量分别控制缓冲区空间和数据数量。
3.3 Sem类封装
#pragma once
#include <semaphore.h>namespace SemModule{int defaultsemval = 1;class Sem{public:Sem(int value = defaultsemval) : _init_value(value){sem_init(&sem, 0, value);}void P(){::sem_wait(&sem);}void V(){::sem_post(&sem);}~Sem(){::sem_destroy(&sem);}private:sem_t sem;int _init_value;};
}
🏳️🌈四、环形队列的逻辑及实现

4.1 核心设计
这是一个基于 信号量(Semaphore) 和 互斥锁(Mutex) 的线程安全环形队列(Ring Buffer),用于实现 生产者-消费者模型。通过以下机制确保线程安全:
信号量控制:空间信号量(_spacesem)控制可用空间,数据信号量(_datasem)控制可消费数据。
互斥锁保护:生产者和消费者各自拥有独立的锁(_p_lock 和 _c_lock),支持多生产者和多消费者并发操作。
4.2 成员变量

4.3 核心接口
4.3.1 构造函数
RingBuffer(int cap): _ring(cap), // 初始化缓冲区容量_cap(cap), // 记录总容量_p_step(0), // 生产者起始位置_c_step(0), // 消费者起始位置_datasem(0), // 初始无可消费数据_spacesem(cap) {} // 初始空间=总容量
作用:初始化缓冲区及相关同步机制
4.3.2Equeue(生产者接口)
void Equeue(const T &in) {_spacesem.P(); // 等待可用空间(信号量-1){LockGuard lockguard(_p_lock); // 加生产者锁_ring[_p_step] = in; // 写入数据_p_step = (_p_step + 1) % _cap; // 环形移动}_datasem.V(); // 增加可消费数据(信号量+1)
}
流程:
- 申请空间:通过
_spacesem.P()等待缓冲区有空位。 - 生产数据:在锁保护下写入数据并更新生产者索引。
- 通知消费者:通过
_datasem.V()增加可消费数据数量。
4.3.3 Pop(消费者接口)
void Pop(T *out) {_datasem.P(); // 等待可消费数据(信号量-1){LockGuard lockguard(_c_lock); // 加消费者锁*out = _ring[_c_step]; // 读取数据_c_step = (_c_step + 1) % _cap; // 环形移动}_spacesem.V(); // 增加可用空间(信号量+1)
}
流程:
- 申请数据:通过
_datasem.P()等待缓冲区有数据。 - 消费数据:在锁保护下读取数据并更新消费者索引。
- 通知生产者:通过
_spacesem.V()增加可用空间数量。
4.4 同步机制
4.4.1 信号量控制

- 生产者阻塞条件:
_spacesem为0时(缓冲区满)。 - 消费者阻塞条件:
_datasem为0时(缓冲区空)。
4.4.2 互斥锁保护

4.5 整体代码
#include "Cond.hpp"
#include "Mutex.hpp"
#include "Sem.hpp"#include <vector>
#include <pthread.h>namespace RingBufferModule{using namespace LockModule;using namespace CondModule;using namespace SemModule;template<typename T>class RingBuffer{public:RingBuffer(int cap): _ring(cap), // 初始化缓冲区容量_cap(cap), // 记录总容量_p_step(0), // 生产者起始位置_c_step(0), // 消费者起始位置_datasem(0), // 初始无可消费数据_spacesem(cap) // 初始空间=总容量{}// 生产者接口void Equeue(const T &in) {_spacesem.P(); // 等待可用空间(信号量-1){LockGuard lockguard(_p_lock); // 加生产者锁_ring[_p_step] = in; // 写入数据_p_step = (_p_step + 1) % _cap; // 环形移动}_datasem.V(); // 增加可消费数据(信号量+1)}// 消费者接口void Pop(T *out) {_datasem.P(); // 等待可消费数据(信号量-1){LockGuard lockguard(_c_lock); // 加消费者锁*out = _ring[_c_step]; // 读取数据_c_step = (_c_step + 1) % _cap; // 环形移动}_spacesem.V(); // 增加可用空间(信号量+1)}// 析构函数~RingBuffer(){}private:std::vector<T> _ring; // 环,临界资源int _cap; // 环的容量 int _p_step; // 生产者指针位置int _c_step; // 消费者指针位置Mutex _p_lock; // 生产者锁Mutex _c_lock; // 消费者锁Sem _datasem; // 数据信号量Sem _spacesem; // 空间信号量};
}
4.6 测试代码
#include "RingBuffer.hpp"
#include <pthread.h>
#include <unistd.h>
#include <ctime>using namespace RingBufferModule;void *Consumer(void *args)
{RingBuffer<int> *ring_buffer = static_cast<RingBuffer<int> *>(args);while(true){sleep(1);// sleep(1);// 1. 消费数据int data;ring_buffer->Pop(&data);// 2. 处理:花时间std::cout << "消费了一个数据: " << data << std::endl;}
}void *Productor(void *args)
{RingBuffer<int> *ring_buffer = static_cast<RingBuffer<int> *>(args);int data = 0;while (true){// 1. 获取数据:花时间// sleep(1);// 2. 生产数据ring_buffer->Equeue(data);std::cout << "生产了一个数据: " << data << std::endl;data++;}
}int main()
{RingBuffer<int> *ring_buffer = new RingBuffer<int>(5); // 共享资源 -> 临界资源// 单生产,单消费pthread_t c1, p1, c2,c3,p2;pthread_create(&c1, nullptr, Consumer, ring_buffer);pthread_create(&c2, nullptr, Consumer, ring_buffer);pthread_create(&c3, nullptr, Consumer, ring_buffer);pthread_create(&p1, nullptr, Productor, ring_buffer);pthread_create(&p2, nullptr, Productor, ring_buffer);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(c3, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);delete ring_buffer;return 0;
}
👥总结
本篇博文对 【Linux】条件变量封装类及环形队列的实现 做了一个较为详细的介绍,不知道对你有没有帮助呢
觉得博主写得还不错的三连支持下吧!会继续努力的~

相关文章:
【Linux】条件变量封装类及环形队列的实现
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
离线部署kubesphere(已有k8s和私有harbor的基础上)
前言说明:本文是在已有k8s集群和私有仓库harbor上进行离线安装kubesphere;官网的离线教程写都很详细,但是在部署部份把搭建集群和搭建仓库也写一起了,跟着做踩了点坑,这里就记录下来希望可以帮助到需要的xdm。 1.根据官…...
非阻塞IO,fcntl,多路转接,select,poll,epoll,reactor
IO次数会影响程序的效率,在编程中往往会尽量减少IO次数,用以提高程序的效率,例如缓冲区,就是减少IO次数提高效率的一种方式;而IO影响效率的最大原因其实是因为IO等拷贝,在进行IO时往往需要拷贝的数据就绪,或…...
Redis常用的数据结构及其使用场景
字符串(String) string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。 string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据,比如jpg图片或者序列化的对象。 string 类型是 R…...
PhotoShop学习04
1.背景图层 最下面的被锁锁住的图层为背景图层,背景图层充当整个图层的背景,名字标注为背景,无法修改背景图层的排序始终位于图层最底部。 当我想把上方的图层移动到背景图层之后,发现无法移动图层无法移动,把背景图层…...
服务器有2张显卡,在别的虚拟环境部署运行了Xinference,然后又建个虚拟环境再部署一个可以吗?
环境: 云服务器Ubuntu系统 2张 NVIDIA H20 96GB Qwen2.5-VL-72B-Instruct-AWQ Qint4量化 AWQ 是 “Activation - Aware Weight Quantization” 的缩写,即激活感知权重量化。它是一种针对大型模型的先进量化算法,通过在权重量化过程中引入对激活值的感知,最小化量化误差…...
K8s中CPU和Memory的资源管理
资源类型 在 Kubernetes 中,Pod 作为最小的原子调度单位,所有跟调度和资源管理相关的属性都属于 Pod。其中最常用的资源就是 CPU 和 Memory。 CPU 资源 在 Kubernetes 中,一个 CPU 等于 1 个物理 CPU 核或者一个虚拟核,取决于节…...
任务挂起和恢复
任务挂起和恢复API函数 下面用按键和震动传感器验证任务挂起和恢复API函数: PA7接震动传感器,按键引脚为PA0,提前初始化好GPIO引脚 key.c #include "key.h" #include "stm32f10x.h"void KeyInit() {GPIO_InitTypeDef …...
【NLP 55、投机采样加速推理】
目录 一、投机采样 二、投机采样改进:美杜莎模型 流程 改进 三、Deepseek的投机采样 流程 Ⅰ、输入文本预处理 Ⅱ、引导模型预测 Ⅲ、候选集筛选(可选) Ⅳ、主模型验证 Ⅴ、生成输出与循环 骗你的,其实我在意透了 —— 25.4.4 一、…...
如何在 Windows 上安装 Python
Python是一种高级编程语言,由于其简单性、多功能性和广泛的应用范围而变得越来越流行。如何在 Windows 操作系统中安装 Python 的过程相对简单,只需几个简单的步骤。 本文旨在指导您完成在 Windows 计算机上下载和安装 Python 的过程。 如何在 Windows…...
【Groovy快速上手 ONLY ONE】Groovy与Java的核心差异
最近在使用的平台上写脚本的语言是Groovy,所以也学习一下,作为 Java 开发者,Groovy 对我们来说会非常友好,而且它的语法更简洁且支持动态类型,所以其实了解下Java和Groovy的差异点就可以快速上手了,以下是 …...
计算机系统---CPU
定义与功能 中央处理器(Central Processing Unit,CPU),是电子计算机的主要设备之一,是计算机的核心部件。CPU是计算机的运算核心和控制核心,负责执行计算机程序中的指令,进行算术运算、逻辑运算…...
WEB安全--提权思路
一、情形 在我们成功上传webshell到服务器中并拿到权限时,发现我们的权限很低无法执行特定的命令,这时为了能做更多的操作,我们就需要提升权限。 二、方式 2.1、Windows提权 1、普通用户执行systeminfo命令获取服务器的基本信息࿰…...
多layout 布局适配
安卓多布局文件适配方案操作流程 以下为通过多套布局文件适配不同屏幕尺寸/密度的详细步骤,结合主流适配策略及最佳实践总结: 一、创建多套布局资源目录 按屏幕尺寸划分 在 res 目录下创建以下文件夹(根据设备特性自动匹配ÿ…...
selectdb修改表副本
如果想修改doris(也就是selectdb数据库)表的副本数需要首先确定是否分区表,当前没有数据字典得知哪个表是分区的,只能先show partitions看结果 首先,副本数不应该大于be节点数 其次,修改期间最好不要跑业务…...
Metabase:一个免费开源的BI平台
今天给大家介绍一个开源数据可视化分析工具:Metabase。它可以帮助用户快速连接数据库、执行查询并创建交互式仪表盘,即使非技术人员也能快速上手。 Metabase 支持多种数据源,包括 MySQL、PostgreSQL、Oracle、SQL Server、SQLite、MongoDB、P…...
第15届蓝桥杯省赛python组A,B,C集合
过几天就省赛了,一直以来用的是C,Python蓝桥杯也是刚刚开始准备(虽然深度学习用的都是python,但是两者基本没有任何关系),这两天在做去年题时犯了很多低级错误,因此记录一下以便自己复查 PS&am…...
AWS 云运维管理指南
一、总体目标 高可用性:通过跨可用区 (AZ) 和跨区域 (Region) 的架构设计,确保系统运行可靠。性能优化:优化AWS资源使用,提升应用性能。安全合规:利用AWS内置安全服务,满足行业合规要求(如GDPR、ISO 27001、等保2.0)。成本管控:通过成本优化工具,减少浪费,实现FinOp…...
为什么有的深度学习训练,有训练集、验证集、测试集3个划分,有的只是划分训练集和测试集?
在机器学习和深度学习中,数据集的划分方式取决于任务需求、数据量以及模型开发流程的严谨性。 1. 三者划分:训练集、验证集、测试集 目的 训练集(Training Set):用于模型参数的直接训练。验证集(Validati…...
虚拟现实 UI 设计:打造沉浸式用户体验
VR UI 设计基础与特点 虚拟现实技术近年来发展迅猛,其独特的沉浸式体验吸引了众多领域的关注与应用。在 VR 环境中,UI 设计扮演着至关重要的角色,它是用户与虚拟世界交互的桥梁。与传统 UI 设计相比,VR UI 设计具有显著的特点。传…...
前端Uniapp接入UviewPlus详细教程!!!
相信大家在引入UviewPlusUI时遇到很头疼的问题,那就是明明自己是按照官网教程一步一步的走,为什么到处都是bug呢?今天我一定要把这个让人头疼的问题解决了! 1.查看插件市场 重点: 我们打开Dcloud插件市场搜素uviewPl…...
【性能优化点滴】odygrd/quill在编译期做了哪些优化
Quill 是一个高性能的 C 日志库,它在编译器层面进行了大量优化以确保极低的运行时开销。以下是 Quill 在编译器优化方面的关键技术和实现细节: 1. 编译时字符串解析与格式校验 Quill 在编译时完成格式字符串的解析和校验,避免运行时开销&…...
02 反射 泛型(II)
目录 一、反射 1. 反射引入 2. 创建对象 3. 反射核心用法 二、泛型 1. 泛型的重要性 (1)解决类型安全问题 (2)避免重复代码 (3)提高可读性和维护性 2. 泛型用法 (1)泛型类 …...
Spring Boot 七种事务传播行为只有 REQUIRES_NEW 和 NESTED 支持部分回滚的分析
Spring Boot 七种事务传播行为支持部分回滚的分析 支持部分回滚的传播行为 REQUIRES_NEW:始终开启新事务,独立于外部事务,失败时仅自身回滚。NESTED:在当前事务中创建保存点(Savepoint),可局部…...
ZLMediaKit 源码分析——[5] ZLToolKit 中EventPoller之延时任务处理
系列文章目录 第一篇 基于SRS 的 WebRTC 环境搭建 第二篇 基于SRS 实现RTSP接入与WebRTC播放 第三篇 centos下基于ZLMediaKit 的WebRTC 环境搭建 第四篇 WebRTC学习一:获取音频和视频设备 第五篇 WebRTC学习二:WebRTC音视频数据采集 第六篇 WebRTC学习三…...
元宇宙浪潮下,前端开发如何“乘风破浪”?
一、元宇宙对前端开发的新要求 元宇宙的兴起,为前端开发领域带来了全新的挑战与机遇。元宇宙作为一个高度集成、多维互动的虚拟世界,要求前端开发不仅具备传统网页开发的能力,还需要掌握虚拟现实(VR)、增强现实&#…...
2025年3月 Scratch 图形化(二级)真题解析 中国电子学会全国青少年软件编程等级考试
2025.03Scratch图形化编程等级考试二级真题试卷 一、选择题 第 1 题 甲、乙、丙、丁、戊五人参加100米跑比赛,甲说:“我的前面至少有两人,但我比丁快。”乙说:“我的前面是戊。”丙说:“我的后面还有两个人。”请从前往后(按照速度快慢&a…...
【新能源汽车整车动力学模型深度解析:面向MATLAB/Simulink仿真测试工程师的硬核指南】
1. 前言 作为MATLAB/Simulink仿真测试工程师,掌握新能源汽车整车动力学模型的构建方法和实现技巧至关重要。本文将提供一份6000+字的深度技术解析,涵盖从基础理论到Simulink实现的完整流程。内容经过算法优化设计,包含12个核心方程、6大模块实现和3种验证方法,满足SEO流量…...
MCP协议的Streamable HTTP:革新数据传输的未来
引言 在数字化时代,数据传输的效率和稳定性是推动技术进步的关键。MCP(Model Context Protocol)作为AI生态系统中的重要一环,通过引入Streamable HTTP传输机制,为数据交互带来了革命性的变化。本文将深入解读MCP协议的…...
dify中配置使用Ktransformer模型
一共是两个框架一个是Ktransformer,一个是dify。 Ktransformer用来部署LLM,比如Deepseek,而LLm的应用框架平台Dify主要用来快速搭建基于LLM应用。 这篇教程主要是用来介绍两个框架的交互与对接的,不是部署Ktransformer也部署部署Dify,要部署Dify、Ktransformer可以直接参考…...
