当前位置: 首页 > news >正文

【LINUX操作系统】生产者消费者模型(下):封装、信号量与环形队列

1.封装、完善基于阻塞队列的productor-consumer module

前文中我们封装了自己的Mutex 

【LINUX操作系统】线程同步与互斥-CSDN博客

按照老规矩,现在我们对同步与互斥的理解更进一步了,现在把这种面向过程的语言封装成面向对象的写法

 1.1 封装条件变量

#pragma once#include <iostream>
#include <string>
#include <pthread.h>
#include "Mutex.hpp"namespace CondModule
{class Cond{public:Cond(){pthread_cond_init(&_cond,nullptr);}~Cond(){pthread_cond_destroy(&_cond);}void Wait(){}void notify(){pthread_cond_signal(&_cond);}void notifyall(){pthread_cond_broadcast(&_cond);}private:pthread_cond_t _cond;};
}

在上面的代码中,需要注意wait函数,因为让线程在条件变量下等待时,pthread_cond_wait接口会释放线程所持有的锁,所以需要让该接口接收一个参数,用于pthread_cond_wait的第二个参数

                        

再由上一文中的对Mutex的封装:

【LINUX操作系统】线程同步与互斥-CSDN博客

                

1.2 在BlockQueue中使用自己封装的接口

完整的使用自己接口的第二版代码,将代码改成面向对象的风格

//version 2 引入自己的接口
#include <iostream>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include "Mutex.hpp"
#include "Cond.hpp"namespace BQModule
{using namespace CondModule;using namespace LockMoudle;size_t gsize = 5;//整个队列最多装5个数据template<typename T>class BlockQueue{public:bool IsFull(){return _bq.size()==_capacity;}bool IsEmpty(){return _bq.empty();}BlockQueue(): _cwait_num(0),_pwait_num(0),_capacity(gsize){//自己封装的接口都是在定义时就初始化了// pthread_mutex_init(&_lock,nullptr);// pthread_cond_init(&_con_cond,nullptr);// pthread_cond_init(&_pro_cond,nullptr);}void Pop(T* p_data)//designed for Consumer{//pthread_mutex_lock(&_lock);MutexGuard mutexguard(_lock);while(IsEmpty()){_cwait_num++;//bq中已经没有数据,消费者线程需要进入条件变量去等待//pthread_cond_wait(&_con_cond,&_lock);//再次理解为什么wait必须释放锁_con_cond.wait(_lock);_cwait_num--;}*p_data = _bq.front();_bq.pop();//此处,刚刚减少数据,并且还没有解锁,bq中一定没有满,可以唤醒一个生产者线程if(_pwait_num)_pro_cond.notify();//pthread_mutex_unlock(&_lock);}void Enqueue(const T& data)//designed for Productor{//pthread_mutex_lock(&_lock);MutexGuard mutexguard(_lock);while(IsFull()){_pwait_num++;//bq中数据已经满了,生产者需要进入条件变量去等待_pro_cond.wait(_lock);_pwait_num--;}_bq.push(data);//此处,刚刚加入数据,并且还没有解锁,bq中一定有数据,可以去唤醒一个消费者条件变量中的线程if(_cwait_num)//pthread_cond_signal(&_con_cond);_con_cond.notify();//pthread_mutex_unlock(&_lock);}~BlockQueue(){//自己封装的类在析构时都会自动释放资源// pthread_mutex_destroy(&_lock);// pthread_cond_destroy(&_con_cond);// pthread_cond_destroy(&_pro_cond);}private:std::queue<T> _bq;Cond  _pro_cond; //生产者的条件变量Cond _con_cond; //消费者的条件变量Mutex _lock;//一把需要被各生产者和消费者看到的锁,用于互斥size_t _capacity; //容量int _cwait_num;int _pwait_num;};
}

这样一来,所有的锁都不再需要手动释放,而是代码在RAII风格下自动释放锁。 

1.3 实现多生产者多消费者版本

由于我们设计的本来就只有一把锁,消费者与消费者、生产者与生产者这两种关系先天就是互斥且同步的

测试结果:

1.4 传递任务的生产消费模型

交易场所不仅仅用来传递数据,也可以用来传递任务。

假设今天有个Task类,并且按照Task修改主函数中模板:

class Task
{
public:Task(int x = 1, int y = 1): _x(x), _y(y){}~Task() {}void Excute(){_res = _x+_y;}int res(){return _res;}
private:int _x;int _y;int _res;
};
using namespace BQModule;
void *Consumer(void *arg) // 消费者
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(arg);while (true){sleep(2);// 1.从bq中获得数据Task data;bq->Pop(&data); // 输出型参数// 2.处理数据data.Excute();std::cout << "consumer get: " << data.res() << std::endl;}
}void *Productor(void *arg) // 生产者
{BlockQueue<Task> *bq = static_cast<BlockQueue<Task> *>(arg);while (true){// sleep(2);//  1.从外部获取数据//::data+=10;//+=操作不是原子的,可能发生进入了相同数据的情况int x = rand() % 5 + 1; //[1,5]int y = rand() % 9 + 1; //[1,9]Task t(x, y);// 2.将数据入队列bq->Enqueue(t);printf("productor asked  %d+%d=? \n", x, y);}
}int main()
{srand(time(nullptr) ^ getpid());BlockQueue<Task> *bq = new BlockQueue<Task>;// pthread_t p1,p2,c1,c2,c3;pthread_t p1, c1;pthread_create(&p1, nullptr, Productor, (void *)bq);pthread_create(&c1, nullptr, Consumer, (void *)bq);pthread_join(p1, nullptr);pthread_join(c1, nullptr);delete bq;return 0;
}

也可以使用using task_t =function<void()>来实现,直接把task_t当作BlockQueue的类型即可。


2. 进一步理解 生产消费者模型

互斥访问,何以高效?

生产消费模型都是在各种锁或者信号量的控制下进行的,那是否能说明模型中的各个线程都是串行执行的呢?串行执行难道不是有违设计线程的初衷,降低了整体效率吗?

由以上的传递任务的模型不难看出,生产消费模型并不是单纯的用于在阻塞队列中去传递数据(一些网络服务器虽然确实是做这个的),他还有很多其他运用场景,比如:玩卡牌游戏,一个线程用于获得键鼠等外设上的信息(生产者),另一个(可能是好几个)线程用于处理背后的逻辑(消费者),那其实对于整个进程来说,线程A外设上等待信息的时间和线程B处理背后逻辑的时间才是真正花时间的,而等待信息和处理逻辑的时候,显然两个线程是在并行。

超市中去放货取货只是整个进程中损耗很小的一部分,不应该由此担心是不是变成了串行。


3. POSIX信号量

        信号量(Semaphore)是操作系统中用于进程或线程同步的核心工具,其本质是一个非负整数计数器,用于表示共享资源的剩余数量或访问权限的状态。通过计数器的增减操作,信号量可以协调多个进程/线程对临界资源的访问,避免竞争条件(Race Condition)和数据不一致问题。

        在进程间通信的时候我们大致介绍过基于systemV的信号量,现在我们介绍POSIX信号量:

        之前我们的mutex都是对于资源的整体预订,而信号量是一种不用去整体预定的策略。

信号量就像买电影票,买了电影票表示预定了这个座位:一个大数组,我不需要全部预定完,可以只使用一个空间。信号量就是对剩余座位的计数器,表示当下还有多少“部分资源”可供使用。

                        

POSIX的信号量多用于满足线程间同步。

就像mutex的锁一样,信号量sem本身就是临界资源,所以信号量的--(P操作)或者++(V操作)本身必须是原子的。

所以对于锁来说(mutex),其实就是一个二元信号量去控制,也就是对于该信号量:非0即1。整体资源还存在、没被拿就是1,被拿了(在讲锁的原理的时候提到过,本质是swap,是原子性的汇编操作)就是0

  • P操作(sem_wait:也叫等待信号量
    减少信号量的值。若结果为负,则阻塞调用线程(或进程,取决于信号量类型),直到信号量非负。
    • 函数原型:
      int sem_wait(sem_t *sem);
    • 行为:
      • 若信号量值 > 0,则减1并继续执行。
      • 若信号量值 = 0,则线程被挂起,直到其他线程调用sem_post增加信号量值。
  • V操作(sem_post:也叫发布信号量
    增加信号量的值。若信号量原本为0(即有线程因sem_wait被阻塞),则唤醒一个阻塞的线程。
    • 函数原型:
      int sem_post(sem_t *sem);
    • 行为:
      • 信号量值加1。
      • 若存在因sem_wait阻塞的线程,则唤醒其中一个。

顺便再把semaphore中其他常用的接口也记录一下(semaphore头文件编译的时候也需要加上-lpthread)

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
pshared:0表⽰线程间共享,⾮零表⽰进程间共享
value:信号量初始值销毁信号量
int sem_destroy(sem_t *sem);

 value表示一开始有的“部分资源”的数量。比如这种情况就该设置为16,如果已经有一个被使用就设置成15。

                

中间的参数都默认设置成0即可。


4. 基于环形队列的⽣产消费模型

        循环队列版本的生产消费模型本质就是利用了信号量实现并发处理,在前面的阻塞队列版本,不论是单生产、消费线程版本还是多生产、消费线程版本,都无法做到生产的同时消费,但是实际上如果可以以信号量来控制资源的使用逻辑就不影响了。

        因此,今天我们选择用一个数组vector来作为存放数据的基本容器

循环队列原理

head是循环队列的头,tail是循环队列的尾。整个队列为空时,head和tail同时指向同一个位置(如图)。一旦开始插入数据,head指向下一个会被使用的数据(也就是整个队列的第一个数据,队列的头,消费者下一次就从head处拿出数据),tail指向下一个即将插入数据的位置(也就是整个队列的第一个空位置,生产者生产出数据就放在tail指向的位置),整个循环队列满的时候,head和tail也都指向同一个位置。可以用取模的方法保证tail会去转圈。

                ​​​​​​​        

资源,分为数据空间。

对于生产者来说,tail从0开始,对空间资源进行P操作(--),对数据资源进行V操作(++)

对于消费者来说,head从0开始,对空间资源进行V操作,对数据资源进行P操作

        ​​​​​​​        

分析:刚开始没有数据的时候,head和tail指向同一个位置,难道不会让两个线程发生竞态条件吗?此时没有数据,数据资源的计数器----数据资源的信号量为0,该信号量自动不让消费者进入,保证生产者先进行原子性生产(对空间资源进行V操作)

最后数据满的时候,head和tail又指向同一个位置,此时没有空间了,所有的空间都有数据,空间资源的计数器----空间资源的信号量为0,该信号量自动不让生产者进入,保证消费者先进性原子性消费(对数据进行P操作)。

注意,至于每次等待的队列到底是FIFO还是随机争抢,这个取决于操作系统的实现方法 

代码实现

借鉴下之前写BlockQueue的主程序,稍加改造

【LINUX操作系统】生产者消费者模型(上):概念与阻塞队列-CSDN博客

(记得再改改变量名,bq是blockqueue的缩写......)

构建ringbuffer类:

 

namespace RingBufferModule
{template <typename T>class ringbuffer{public:ringbuffer(int init_sem_num=DEFAULTSENMVAL):_p_pos(0),_c_pos(0),_ring(SIZE),_size(SIZE){int n = sem_init(&_data_sem,0,init_sem_num);if(n<0){std::cerr<<"sem init error"<<std::endl;}int m = sem_init(&_space_sem,0,init_sem_num);if(m<0){std::cerr<<"sem init error"<<std::endl;}}void Enqueue(const T& in)//给生产者用{//1.获取数据//2.入环}void Pop(T* out)//给消费者用{}~ringbuffer(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);}ringbufferprivate:std::vector<T> _ring; //存储数据的基本容器,也是重点临界资源size_t _size; //整个环的大小sem_t _data_sem;//数据资源信号量sem_t _space_sem;//空间资源信号量int _p_pos;//生产者下标位置,即tail,表示下一次放数据的地方int _c_pos;//消费者下标位置,即head,表示下一次取数据的地方};
}

对于这个循环队列,需要有Enqueue和Pop的接口,分别给生产者和消费者用于放数据。

前几次都是先用最基本的C库接口封装,然后自己封装一个面向对象的类,再替换接口,这次直接先封装,再调接口。

就可以改变我们刚刚的构造函数

并且完成具体逻辑的函数,将各种sem_wait还有sem_post就都封装起来了。

在一开始数据为空的时候,不用担心消费者进来,因为此时_data_sem无法进行P操作,会阻塞在原地。

测试

这样,我们的循环队列保证在为空为满时都是进行“互斥与同步”,而在其余时刻,各自访问各自的信号量,提升了一丢丢的效率。


5. 多生产多消费的循环队列生产消费模型 

        再加入其他生产者消费者之后,关系又恢复成了需要控制:生产者与生产者、消费者与消费者,生产者与消费者。我们采取如下思路:给生产者们加一个锁,所有的生产者竞争出来一个线程去参加这次循环队列的sem的PV操作;给所有的消费者一个锁,所有的消费者竞争出来一个线程去参加这次循环队列的sem的PV操作。

依然是直接引进我们自己实现的RAII风格的锁。【LINUX操作系统】线程同步与互斥-CSDN博客

现在的问题是,锁在哪里加效率更高呢?

比如Pop,是在MutexGuard后使用data_sem.P()还是在MutexGuard之前呢? 

        

两者都能完成只竞争出一个进入循环队列的栈,区别在于:

在1,先锁住,只有一个去进行数据信号量的“取电影票”,然后正常执行。

在2,先让可以取电影票的线程(消费者们)都取一张电影票,然后再锁住,依次让人进入电影院获取资源。自然,方案2效率更高。一次性的并发_data_sem.P()可以减少之后串行调用该接口的时间。

        这样依然不担心生产者与消费者的互斥,因为能拿到票这一步已经天然的构成了一道屏障,只要拿到了票就一定不会冲突,可以各自使用各自的部分资源。

最终代码:

#define SIZE 10using namespace LockMoudle;namespace RingBufferModule
{template <typename T>class ringbuffer{public:ringbuffer(): _p_pos(0), _c_pos(0), _ring(SIZE), _size(SIZE), _data_sem(0), _space_sem(SIZE){}void Enqueue(const T &in) // 给生产者用{_space_sem.P();{MutexGuard lockguard(_p_mutex);_ring[_p_pos] = in;_p_pos++;_p_pos %= _size;}_data_sem.V();}void Pop(T *out) // 给消费者用{// MutexGuard lockguard(_c_mutex);//锁在这里1_data_sem.P();{MutexGuard lockguard(_c_mutex); // 锁在这里2*out = _ring[_c_pos];_c_pos++;_c_pos %= _size;}_space_sem.V();}~ringbuffer(){// 都自动销毁了}private:std::vector<T> _ring; // 存储数据的基本容器,也是重点临界资源size_t _size;         // 整个环的大小int _p_pos;           // 生产者下标位置,即tail,表示下一次放数据的地方int _c_pos;           // 消费者下标位置,即head,表示下一次取数据的地方Sem _data_sem;  // 数据资源信号量Sem _space_sem; // 空间资源信号量Mutex _p_mutex;Mutex _c_mutex;};
}

相关文章:

【LINUX操作系统】生产者消费者模型(下):封装、信号量与环形队列

1.封装、完善基于阻塞队列的productor-consumer module 前文中我们封装了自己的Mutex 【LINUX操作系统】线程同步与互斥-CSDN博客 按照老规矩&#xff0c;现在我们对同步与互斥的理解更进一步了&#xff0c;现在把这种面向过程的语言封装成面向对象的写法 1.1 封装条件变量 #p…...

项目管理学习-CSPM-4考试总结

前言 经过两个月左右时间的学习&#xff0c;今天&#xff08;2025年5月17日&#xff09;参加了CSPM-4的考试&#xff0c;仿佛回到了2011年参加软考高项的时候。中午12点考完出来后&#xff0c;手都是酸酸的。不过整体感觉还可以&#xff0c;和预想的差不多。CSPM-4的考试一共有…...

自己手写tomcat项目

一&#xff1a;Servlet的原理 在Servlet(接口中)有&#xff1a; 1.init():初始化servlet 2.getServletConfig()&#xff1a;获取当前servlet的配置信息 3.service():服务器&#xff08;在HttpServlet中实现&#xff0c;目的是为了更好的匹配http的请求方式&#xff09; 4.g…...

C语言—再学习(结构体)

一、建立结构体 用户自己建立由不同类型数据组成的组合型的数据结构&#xff0c;它称为结构体。 struct Student { int num; //学号char name[20]; //名字为字符串char sex; //性别int age; //年纪float score; //分数char addr[30]; 地址为字符…...

SpringBoot--自动配置原理详解

为什么要学习自动配置原理&#xff1f; 原因&#xff1a;在实际开发中&#xff0c;我们经常会定义一些公共的组件&#xff0c;提供各个团队来使用&#xff0c;为了使用方便&#xff0c;我们经常会将公共的组件自定义成starter&#xff0c;如果想自定义starter&#xff0c;必须…...

MiInsertPageInFreeList函数分析和MmFreePagesByColor数组的关系

第一部分&#xff1a; Color MI_GET_COLOR_FROM_LIST_ENTRY(PageFrameIndex, Pfn1); ColorHead &MmFreePagesByColor[ListName][Color]; 第二部分&#xff1a; #define MI_GET_COLOR_FROM_LIST_ENTRY(index,pfn) \ ((ULONG)(((pfn)->…...

Windows/MacOS WebStorm/IDEA 中开发 Uni-App 配置

文章目录 前言1. 安装 HBuilder X2. WebStorm/IDEA 安装 Uniapp Tool 插件3. 配置 Uniapp Tool 插件4. 运行 Uni-App 项目 前言 前端开发人员对 WebStorm 一定不陌生&#xff0c;但有时需要开发 Uni-App 的需求&#xff0c;就必须要采用 HBuilder X&#xff0c;如果不习惯 HBu…...

redisson分布式锁实现原理归纳总结

Redisson 分布式锁的实现原理主要依赖于 Redis 的 Hash 数据结构、Lua 脚本、发布订阅机制以及看门狗&#xff08;Watchdog&#xff09;机制&#xff0c;以下是核心要点总结&#xff1a; 1. 核心原理 • 互斥性与可重入性&#xff1a; 通过 Redis 的 Hash 数据结构保存锁的持…...

Ubuntu 添加系统调用

实验内容 通过内核编译法添加一个不用传递参数的系统调用&#xff0c;其功能可自定义。 &#xff08;1&#xff09;添加系统调用号&#xff0c;系统会根据这个号找到syscall_table中的相应表项。具体做法是在syscall_64.tbl文件中添加系统调用号和调用函数的对应关系。 &#…...

Olib 2.2.0 | 免费开源软件,无需注册登录即可从ZLibrary下载多语言电子书

Olib是一款专为书籍爱好者设计的免费开源软件&#xff0c;它允许用户无需注册或登录即可从ZLibrary高速下载各种语言的电子书。该软件支持上百种语言的电子书下载&#xff0c;非常适合需要多语言资源的读者和研究人员使用。Olib的操作界面非常直观&#xff0c;使得书籍的搜索与…...

c++动态链接库

1. 生成动态链接库 首先实现一个动态链接库的代码 // example.cpp #include <iostream> void sayHello() {std::cout << "Hello from shared library!" << std::endl; }int add(int a, int b) {return a b; }// example.h #pragma once void sa…...

HelloWorld

HelloWorld 新建一个java文件 文件后缀名为 .javahello.java【注意】系统可能没有显示文件后缀名&#xff0c;我们需要手动打开 编写代码 public class hello {public static void main(String[] args) {System.out.print(Hello,World)} }编译 javac java文件&#xff0c;会生…...

SVGPlay:一次 CodeBuddy 主动构建的动画工具之旅

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 背景与想法 我一直对 SVG 图标的动画处理有浓厚兴趣&#xff0c;特别是描边、渐变、交互等效果能为图标增添许…...

SLAM定位常用地图对比示例

序号 地图类型 概述 1 格栅地图 将现实环境栅格化,每一个栅格用 0 和 1 分别表示空闲和占据状态,初始化为未知状态 0.5 2 特征地图 以点、线、面等几何特征来描绘周围环境,将采集的信息进行筛选和提取得到关键几何特征 3 拓扑地图 将重要部分抽象为地图,使用简单的图形表示…...

强化学习中,frames(帧)和 episodes(回合)

在强化学习中&#xff0c;frames&#xff08;帧&#xff09;和 episodes&#xff08;回合&#xff09;是两个不同的概念&#xff1a; 1. 定义差异 Frame&#xff08;帧&#xff09;&#xff1a; 表示智能体与环境交互的单个时间步&#xff08;step&#xff09;&#xff0c;例如…...

HCIP第六次作业

一、拓扑图 二、需求 1、使用PreVal策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2、使用AS_Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3、配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4、使用Local Preference策略&#xff0c;确保R1通过R2…...

高频面试题(含笔试高频算法整理)基本总结回顾110

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…...

数据湖与数据仓库融合:Hudi、Iceberg、Delta Lake 实践对比

在实时与离线一体化的今天,数据湖与数据仓库边界不断融合,越来越多企业选用如 Hudi、Iceberg、Delta Lake 等开源方案实现统一的数据存储、计算、分析平台。本篇将围绕以下关键点,展开实战对比与解决方案分享: ✅ 实时写入能力 ✅ ACID 保证 ✅ 增量数据处理能力 ✅ 流批一…...

OGG 更新表频繁导致进程中断,见鬼了?非也!

大家好&#xff0c;这里是 DBA学习之路&#xff0c;专注于提升数据库运维效率。 目录 前言问题描述问题分析解决方案后续 前言 最近几周一直遇到一个 OGG 问题&#xff0c;有一张表已更新就会中断 OGG 同步进程&#xff0c;本文记录一下分析过程以及解决方案。 问题描述 昨天…...

C++学习-入门到精通-【7】类的深入剖析

C学习-入门到精通-【7】类的深入剖析 类的深入剖析 C学习-入门到精通-【7】类的深入剖析一、Time类的实例研究二、组成和继承三、类的作用域和类成员的访问类作用域和块作用域圆点成员选择运算符(.)和箭头成员选择运算符(->)访问函数和工具函数 四、具有默认实参的构造函数重…...

非易失性存储技术综合对比:EEPROM、NVRAM、NOR Flash、NAND Flash和SD卡

非易失性存储技术综合对比&#xff1a;EEPROM、NVRAM、NOR Flash、NAND Flash和SD卡 读写性能对比 存储类型读取速度写入速度随机访问能力最小操作单位NVRAM极快(~10ns)极快(~10ns)极优(字节级)字节EEPROM中等(~100ns)慢(~5ms/字节)优(字节级)字节NOR Flash快(~50ns)慢(~5ms/…...

数字化转型- 数字化转型路线和推进

数字化转型三个阶段 百度百科给出的企业的数字化转型包括信息化、数字化、数智化三个阶段 信息化是将企业在生产经营过程中产生的业务信息进行记录、储存和管理&#xff0c;通过电子终端呈现&#xff0c;便于信息的传播与沟通。数字化通过打通各个系统的互联互通&#xff0c;…...

ARM (Attention Refinement Module)

ARM模块【来源于BiSeNet】&#xff1a;细化特征图的注意力&#xff0c;增强重要特征并抑制不重要的特征。 Attention Refinement Module (ARM) 详解 ARM (Attention Refinement Module) 是 BiSeNet 中用于增强特征表示的关键模块&#xff0c;它通过注意力机制来细化特征图&…...

符合Python风格的对象(对象表示形式)

对象表示形式 每门面向对象的语言至少都有一种获取对象的字符串表示形式的标准方 式。Python 提供了两种方式。 repr()   以便于开发者理解的方式返回对象的字符串表示形式。str()   以便于用户理解的方式返回对象的字符串表示形式。 正如你所知&#xff0c;我们要实现_…...

AtCoder AT_abc406_c [ABC406C] ~

前言 除了 A 题&#xff0c;唯一一道一遍过的题。 题目大意 我们定义满足以下所有条件的一个长度为 N N N 的序列 A ( A 1 , A 2 , … , A N ) A(A_1,A_2,\dots,A_N) A(A1​,A2​,…,AN​) 为波浪序列&#xff1a; N ≥ 4 N\ge4 N≥4&#xff08;其实满足后面就必须满足这…...

多指标组合策略

该策略(MultiConditionStrategy)是一种基于多种技术指标和市场条件的交易策略。它通过综合考虑多个条件来生成交易信号,从而决定买入或卖出的时机。 以下是对该策略的详细分析: 交易逻辑思路 1. 条件1:星期几和价格变化判断 - 该条件根据当前日期是星期几以及价格的变化…...

系统架构-大数据架构设计

基础介绍 三大挑战&#xff1a; 如何处理非结构化和半结构化数据如何探索大数据复杂性、不确定性特征描述的刻画方法及大数据的系统建模数据异构性与决策异构性的关系对大数据知识发现与管理决策的影响 架构特征&#xff1a; 鲁棒性&#xff08;稳定性&#xff09;和容错性…...

R语言空间数据处理入门教程

我的课程《R语言空间数据处理入门教程》已重新恢复课程售卖&#xff0c;有需要的读者可以学习。 &#x1f447;点击下方链接&#xff08;文末“阅读原文”可直达&#xff09;&#xff0c;立即开启你的空间数据之旅&#xff1a; https://www.bilibili.com/cheese/play/ss13775…...

QT+EtherCAT 主站协议库—SOEM主站

SOEM 是 Simple Open EtherCAT Master Library 的缩写&#xff0c;是瑞典 rt-lab 提供 的一个开源 EtherCAT 主站协议库 。 SOEM 库使用 C 语言编写&#xff0c;可以在 windows 以及 Linux 平台上运行&#xff0c;并也可以方便地移植到嵌入式平台上。 SOEM 支持 CoE &#xff0…...

Java-反射(Reflection)

一&#xff1a;概述 &#xff08;1&#xff09;出现背景 &#xff08;2&#xff09;解决方案 &#xff08;3&#xff09;使用场景 业务开发用的少&#xff0c;框架使用的多&#xff0c;业务反射被认为是动态语言的关键 &#xff08;4&#xff09;与原方法对比 &#xff08;5…...

第一次经历项目上线

这几天没写csdn&#xff0c;因为忙着项目上线的问题&#xff0c;我这阶段改了非常多的前端bug哈哈哈哈&#xff0c;说几个比较好的bug思想&#xff01; 这个页面算是我遇到的比较大的bug&#xff0c;因为我一开始的逻辑都写好了&#xff0c;询价就是在点击快递公司弹出弹框的时…...

基于C#的MQTT通信实战:从EMQX搭建到发布订阅全解析

MQTT(Message Queueing Telemetry Transport) 消息队列遥测传输&#xff0c;在物联网领域应用的很广泛&#xff0c;它是基于Publish/Subscribe模式&#xff0c;具有简单易用&#xff0c;支持QoS&#xff0c;传输效率高的特点。 它被设计用于低带宽&#xff0c;不稳定或高延迟的…...

DeepSeek超大模型的高效训练策略

算力挑战 训练DeepSeek此类千亿乃至万亿级别参数模型,对算力资源提出了极高要求。以DeepSeek-V3为例,其基础模型参数量为67亿,采用专家混合(MoE)架构后实际激活参数可达几百亿。如此规模的模型远超单张GPU显存容量极限,必须借助分布式并行才能加载和训练。具体挑战主要包…...

【论文阅读】人脸修复(face restoration ) 不同先验代表算法整理

转眼做人脸复原(face restoration)算法也一段时间了&#xff0c;根据自己的记忆整理一下自己的一些看法&#xff0c;算作个人记录&#xff0c;当然如果有人愿意分享自己的看法也是极好的。先挂下文章链接&#xff0c;下一篇在写总结。 一、前述 人脸修复(face restoration)任…...

最小二乘法拟合平面(线性回归法、梯度下降、PCA法)

参考笔记&#xff1a; Open3D 最小二乘拟合平面&#xff08;直接求解法&#xff09;【2025最新版】_python open3d已知平面方程绘制平面-CSDN博客 目录 1.前言 2.线性回归法 2.1 模型假设 2.2 定义误差函数 2.3 求偏导并解方程 2.4 解方程 2.5 案例演示 2.5.1 手工计…...

数组名既可作为指针也可作为变量名

在C语言中&#xff0c;数组名在不同的上下文中既可以作为指向数组首个元素的指针&#xff0c;也可以代表整个数组&#xff0c;这是由C语言的设计和语法规则决定的&#xff0c;下面我来详细解释一下。 1. 数组名作为指向首元素的指针 在大多数情况下&#xff0c;当数组名出现在…...

MySQL相关

1.多表查询关键点在哪 &#x1f4d6; 1️⃣ 明确关联关系 先搞清楚多表之间的关联关系&#xff1a; 一对一&#xff08;1:1&#xff09; 一对多&#xff08;1:N&#xff09; 多对多&#xff08;M:N&#xff09; 比如&#xff1a; 一个课程对应一个教室&#xff08;1:1&am…...

Axure制作可视化大屏动态滚动列表教程

在可视化大屏设计中&#xff0c;动态滚动列表是一种常见且实用的展示方式&#xff0c;能够有效地展示大量信息。本文将详细介绍如何使用Axure制作一个动态滚动的列表展示模块。 一、准备工作 打开Axure软件&#xff1a;确保你已经安装并打开了Axure RP软件。创建新项目&#x…...

计算机网络(1)——概述

1.计算机网络基本概念 1.1 什么是计算机网络 计算机网络的产生背景 在计算机网络出现之前&#xff0c;计算机之间都是相互独立的&#xff0c;每台计算机只能访问自身存储的数据&#xff0c;无法与其他计算机进行数据交换和资源共享。这种独立的计算机系统存在诸多局限性&#…...

融智学视域下的系统性认知增强框架——基于文理工三类AI助理赋能HI四阶跃迁路径

融智学视域下的系统性认知增强框架 ——基于文理工三类AI助理赋能HI四阶跃迁路径 一、如何排除50个认知偏差&#xff1a;消除50类偏差的精准矫正系统 1. 技术架构 文科AI&#xff1a; 构建文化语义场&#xff08;Cultural Semantic Field, CSF&#xff09;&#xff0c;通过…...

C++ - 仿 RabbitMQ 实现消息队列(2)(Protobuf 和 Muduo 初识)

C - 仿 RabbitMQ 实现消息队列&#xff08;2&#xff09;&#xff08;Protobuf 和 Muduo 初识&#xff09; Protobuf1. 序列化/反序列化方法&#xff08;最核心&#xff09;_InternalSerialize()_InternalParse() 2. 内存管理方法SharedCtor()/SharedDtor()InternalSwap() 3. 字…...

FTP与NFS服务实战:从配置到应用

一、FTP服务进阶&#xff1a;客户端工具与访问控制 1. FTP客户端工具对比 在Linux中&#xff0c;ftp和lftp是常用的FTP客户端工具&#xff0c;功能各有侧重&#xff1a; 工具特点适用场景ftp基础命令交互&#xff0c;需手动输入用户名/密码简单文件传输lftp支持多协议、批量…...

高考AI试题查询系统

高考AI试题查询系统 gitee&#xff1a;https://gitee.com/ltyyyds26/GaoKao_AI 数据 来源&#xff1a;OpenLMLab/GAOKAO-Bench: GAOKAO-Bench is an evaluation framework that utilizes GAOKAO questions as a dataset to evaluate large language models. (github.com) 数…...

记录算法笔记(2025.5.17)验证二叉搜索树

给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例 1&#xff1a; 输入&…...

DataX:一个开源的离线数据同步工具

DataX 是一个异构数据源离线同步&#xff08;ETL&#xff09;工具&#xff0c;实现了包括关系型数据库(MySQL、Oracle 等)、HDFS、Hive、ODPS、HBase、FTP 等各种异构数据源之间稳定高效的数据同步功能。它也是阿里云 DataWorks 数据集成功能的开源版本。 为了解决异构数据源同…...

剑指offer第一周

目录 二维数组中的查找 旋转数组的最小数字 调整数组顺序使奇数位于偶数前面 数组中出现次数超过一半的数字 替换空格 从尾到头打印链表 重建二叉树 矩形覆盖 链表中倒数最后k个结点 二进制中1的个数 合并两个排序的链表 树的子结构 二叉树的镜像 ​​​​​​​二…...

素数筛(欧拉筛算法)

#include<bits/stdc.h> using namespace std; #define maxn 100000 int vis[maxn]; int prime[maxn]; //欧拉筛函数 int Euler_sieve(int n) { int i,j,k; k0;//保存素数的个数 memset(vis,0,sizeof(int)*maxn);//初始化数组 for(i2;i<n;i) { if(vis[i]0)//i是素数…...

遨游科普:三防平板是什么?有什么功能?

清晨的露珠还挂在帐篷边缘&#xff0c;背包里的三防平板却已开机导航&#xff1b;工地的尘土飞扬中&#xff0c;工程师正通过它查看施工图纸&#xff1b;暴雨倾盆的救援现场&#xff0c;应急队员用它实时回传灾情数据……这些看似科幻的场景&#xff0c;正因三防平板的普及成为…...

CSS 浮动与定位以及定位中z-index的堆叠问题

CSS 浮动与定位以及定位中z-index的堆叠问题 一、浮动布局的特点与应用 1. 浮动核心特性 脱离标准流&#xff1a;浮动元素会脱离文档流。环绕特性&#xff1a;后续内容会环绕浮动元素排列自动换行&#xff1a;多个浮动元素在容器宽度不足时自动换行 .float-box {float: lef…...

在Maven中替换文件内容的插件和方法

在Maven中替换文件内容的插件和方法 Maven提供了几种方式来替换文件内容&#xff0c;以下是常用的插件和方法&#xff1a; 1. maven-replacer-plugin (推荐) 这是专门用于文件内容替换的插件&#xff0c;功能强大且灵活。 基本配置 <plugin><groupId>com.goog…...