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

生产者消费者模型线程池(纯代码)

目录

生产者消费者模型

条件变量&&互斥锁(阻塞队列)

makefile

Task.hpp

BlockQueue.hpp

BlockQueueTest.cc

信号量&&互斥锁(环形队列)

makefile

RingQueue.hpp

RingQueueTest.cc

线程池(封装任务) 

makefile

Lock.hpp(RAII)

Log.hpp

Task.hpp

ThreadPool.hpp

ThreadPoolTest.cc


生产者消费者模型

条件变量&&互斥锁(阻塞队列)

makefile

CC=g++
FLAGS=-std=c++11
LD=-lpthread
bin=blockQueue
src=BlockQueueTest.cc$(bin):$(src)$(CC) -o $@ $^ $(LD) $(FLAGS)
.PHONY:clean
clean:rm -f $(bin)

Task.hpp

#pragma once#include <iostream>
#include <string>class Task
{
public:Task() : elemOne_(0), elemTwo_(0), operator_('0'){}Task(int one, int two, char op) : elemOne_(one), elemTwo_(two), operator_(op){}int operator() (){return run();}int run(){int result = 0;switch (operator_){case '+':result = elemOne_ + elemTwo_;break;case '-':result = elemOne_ - elemTwo_;break;case '*':result = elemOne_ * elemTwo_;break;case '/':{if (elemTwo_ == 0){std::cout << "div zero, abort" << std::endl;result = -1;}else{result = elemOne_ / elemTwo_;}}break;case '%':{if (elemTwo_ == 0){std::cout << "mod zero, abort" << std::endl;result = -1;}else{result = elemOne_ % elemTwo_;}}break;default:std::cout << "非法操作: " << operator_ << std::endl;break;}return result;}int get(int *e1, int *e2, char *op){*e1 = elemOne_;*e2 = elemTwo_;*op = operator_;}
private:int elemOne_;int elemTwo_;char operator_;
};

BlockQueue.hpp

#pragma once#include <iostream>
#include <queue>
#include <cstdlib>
#include <unistd.h>
#include <pthread.h>using namespace std;const uint32_t gDefaultCap = 5;template <class T>
class BlockQueue
{
public:BlockQueue(uint32_t cap = gDefaultCap) : cap_(cap){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&conCond_, nullptr);pthread_cond_init(&proCond_, nullptr);}~BlockQueue(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&conCond_);pthread_cond_destroy(&proCond_);}public://生产接口void push(const T &in) // const &: 纯输入{// 加锁// 判断->是否适合生产->bq是否为满->程序员视角的条件->1. 满(不生产) 2. 不满(生产)// if(满) 不生产,休眠// else if(不满) 生产,唤醒消费者// 解锁lockQueue();while (isFull()) // ifFull就是我们在临界区中设定的条件{// before: 当我等待的时候,会自动释放mutex_proBlockWait(); //阻塞等待,等待被唤醒。 被唤醒 != 条件被满足(概率虽然很小),被唤醒 && 条件被满足// after: 当我醒来的时候,我是在临界区里醒来的!!}// 条件满足,可以生产pushCore(in); //生产完成// wakeupCon(); // 唤醒消费者unlockQueue();wakeupCon(); // 唤醒消费者}//消费接口T pop(){// 加锁// 判断->是否适合消费->bq是否为空->程序员视角的条件->1. 空(不消费) 2. 有(消费)// if(空) 不消费,休眠// else if(有) 消费,唤醒生产者// 解锁lockQueue();while (isEmpty()){conBlockwait(); //阻塞等待,等待被唤醒,?}// 条件满足,可以消费T tmp = popCore();unlockQueue();wakeupPro(); // 唤醒生产者return tmp;}private:void lockQueue(){pthread_mutex_lock(&mutex_);}void unlockQueue(){pthread_mutex_unlock(&mutex_);}bool isEmpty(){return bq_.empty();}bool isFull(){return bq_.size() == cap_;}void proBlockWait() // 生产者一定是在临界区中的!{// 1. 在阻塞线程的时候,会自动释放mutex_锁pthread_cond_wait(&proCond_, &mutex_);}void conBlockwait() //阻塞等待,等待被唤醒{// 1. 在阻塞线程的时候,会自动释放mutex_锁pthread_cond_wait(&conCond_, &mutex_);// 2. 当阻塞结束,返回的时候,pthread_cond_wait,会自动帮你重新获得mutex_,然后才返回// 为什么我们上节课,写的代码,批量退出线程的时候,发现无法退出?}void wakeupPro() // 唤醒生产者{pthread_cond_signal(&proCond_);}void wakeupCon() // 唤醒消费者{pthread_cond_signal(&conCond_);}void pushCore(const T &in){bq_.push(in); //生产完成}T popCore(){T tmp = bq_.front();bq_.pop();return tmp;}private:uint32_t cap_;           //容量queue<T> bq_;            // blockqueuepthread_mutex_t mutex_;  //保护阻塞队列的互斥锁pthread_cond_t conCond_; // 让消费者等待的条件变量pthread_cond_t proCond_; // 让生产者等待的条件变量
};

BlockQueueTest.cc

#include "Task.hpp"
#include "BlockQueue.hpp"#include <ctime>const std::string ops = "+-*/%";// 并发,并不是在临界区中并发(一般),而是生产前(before blockqueue),消费后(after blockqueue)对应的并发void *consumer(void *args)
{BlockQueue<Task> *bqp = static_cast<BlockQueue<Task> *>(args);while (true){Task t = bqp->pop(); // 消费任务int result = t();    //处理任务 --- 任务也是要花时间的!int one, two;char op;t.get(&one, &two, &op);cout << "consumer[" << pthread_self() << "] " << (unsigned long)time(nullptr) << " 消费了一个任务: " << one << op << two << "=" << result << endl;}
}
void *productor(void *args)
{BlockQueue<Task> *bqp = static_cast<BlockQueue<Task> *>(args);while (true){// 1. 制作任务 --- 要不要花时间?? -- 网络,磁盘,用户int one = rand() % 50;int two = rand() % 20;char op = ops[rand() % ops.size()];Task t(one, two, op);// 2. 生产任务bqp->push(t);cout << "producter[" << pthread_self() << "] " << (unsigned long)time(nullptr) << " 生产了一个任务: " << one << op << two << "=?" << endl;sleep(1);}
}int main()
{srand((unsigned long)time(nullptr) ^ getpid());// 定义一个阻塞队列// 创建两个线程,productor, consumer// productor -----  consumer// BlockQueue<int> bq;// bq.push(10);// int a = bq.pop();// cout << a << endl;// 既然可以使用int类型的数据,我们也可以使用自己封装的类型,包括任务// BlockQueue<int> bq;BlockQueue<Task> bq;pthread_t c, p;pthread_create(&c, nullptr, consumer, &bq);pthread_create(&p, nullptr, productor, &bq);pthread_join(c, nullptr);pthread_join(p, nullptr);return 0;
}

信号量&&互斥锁(环形队列)

makefile

CC=g++
FLAGS=-std=c++11
LD=-lpthread
bin=ringQueue
src=RingQueueTest.cc$(bin):$(src)$(CC) -o $@ $^ $(LD) $(FLAGS)
.PHONY:clean
clean:rm -f $(bin)

RingQueue.hpp

#pragma once#include <iostream>
#include <vector>
#include <string>
#include <semaphore.h>using namespace std;const int gCap = 10;template <class T>
class RingQueue
{
public:RingQueue(int cap = gCap): ringqueue_(cap), pIndex_(0), cIndex_(0){// 生产sem_init(&roomSem_, 0, ringqueue_.size());// 消费sem_init(&dataSem_, 0, 0);pthread_mutex_init(&pmutex_ ,nullptr);pthread_mutex_init(&cmutex_ ,nullptr);}// 生产void push(const T &in){sem_wait(&roomSem_); //无法被多次的申请pthread_mutex_lock(&pmutex_);ringqueue_[pIndex_] = in; //生产的过程pIndex_++;   // 写入位置后移pIndex_ %= ringqueue_.size(); // 更新下标,保证环形特征pthread_mutex_unlock(&pmutex_);sem_post(&dataSem_);}// 消费T pop(){sem_wait(&dataSem_);pthread_mutex_lock(&cmutex_);T temp = ringqueue_[cIndex_];cIndex_++;cIndex_ %= ringqueue_.size();// 更新下标,保证环形特征pthread_mutex_unlock(&cmutex_);sem_post(&roomSem_);return temp;}~RingQueue(){sem_destroy(&roomSem_);sem_destroy(&dataSem_);pthread_mutex_destroy(&pmutex_);pthread_mutex_destroy(&cmutex_);}
private:vector<T> ringqueue_; // 环形队列sem_t roomSem_;       // 衡量空间计数器,productorsem_t dataSem_;       // 衡量数据计数器,consumeruint32_t pIndex_;     // 当前生产者写入的位置, 如果是多线程,pIndex_也是临界资源uint32_t cIndex_;     // 当前消费者读取的位置,如果是多线程,cIndex_也是临界资源pthread_mutex_t pmutex_;pthread_mutex_t cmutex_;
};

RingQueueTest.cc

#include "RingQueue.hpp"
#include <ctime>
#include <unistd.h>// 我们是单生产者,单消费者
// 多生产者,多消费者??代码怎么改?
// 为什么呢???多生产者,多消费者?
// 不要只关心把数据或者任务,从ringqueue 放拿的过程,获取数据或者任务,处理数据或者任务,也是需要花时间的!void *productor(void *args)
{RingQueue<int> *rqp = static_cast<RingQueue<int> *>(args);while(true){int data = rand()%10;rqp->push(data);cout << "pthread[" << pthread_self() << "]" << " 生产了一个数据: " << data << endl;sleep(1);}
}void *consumer(void *args)
{RingQueue<int> *rqp = static_cast<RingQueue<int> *>(args);while(true){//sleep(10);int data = rqp->pop();cout << "pthread[" << pthread_self() << "]" << " 消费了一个数据: " << data << endl;}
}int main()
{srand((unsigned long)time(nullptr)^getpid());RingQueue<int> rq;pthread_t c1,c2,c3, p1,p2,p3;pthread_create(&p1, nullptr, productor, &rq);pthread_create(&p2, nullptr, productor, &rq);pthread_create(&p3, nullptr, productor, &rq);pthread_create(&c1, nullptr, consumer, &rq);pthread_create(&c2, nullptr, consumer, &rq);pthread_create(&c3, nullptr, consumer, &rq);pthread_join(c1, nullptr);pthread_join(c2, nullptr);pthread_join(c3, nullptr);pthread_join(p1, nullptr);pthread_join(p2, nullptr);pthread_join(p3, nullptr);return 0;
}

线程池(封装任务) 

makefile

CC=g++
FLAGS=-std=c++11
LD=-lpthread
bin=threadpool
src=ThreadPoolTest.cc$(bin):$(src)$(CC) -o $@ $^ $(LD) $(FLAGS)
.PHONY:clean
clean:rm -f $(bin)

Lock.hpp(RAII)

#pragma once#include <iostream>
#include <pthread.h>class Mutex
{
public:Mutex(){pthread_mutex_init(&lock_, nullptr);}void lock(){pthread_mutex_lock(&lock_);}void unlock(){pthread_mutex_unlock(&lock_);}~Mutex(){pthread_mutex_destroy(&lock_);}private:pthread_mutex_t lock_;
};class LockGuard
{
public:LockGuard(Mutex *mutex) : mutex_(mutex){mutex_->lock();std::cout << "加锁成功..." << std::endl;}~LockGuard(){mutex_->unlock();std::cout << "解锁成功...." << std::endl;}private:Mutex *mutex_;
};

Log.hpp

#pragma once#include <iostream>
#include <ctime>
#include <pthread.h>std::ostream &Log()
{std::cout << "Fot Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | " << " Thread[" << pthread_self() << "] | ";return std::cout;
}

Task.hpp

#pragma once#include <iostream>
#include <string>class Task
{
public:Task() : elemOne_(0), elemTwo_(0), operator_('0'){}Task(int one, int two, char op) : elemOne_(one), elemTwo_(two), operator_(op){}int operator() (){return run();}int run(){int result = 0;switch (operator_){case '+':result = elemOne_ + elemTwo_;break;case '-':result = elemOne_ - elemTwo_;break;case '*':result = elemOne_ * elemTwo_;break;case '/':{if (elemTwo_ == 0){std::cout << "div zero, abort" << std::endl;result = -1;}else{result = elemOne_ / elemTwo_;}}break;case '%':{if (elemTwo_ == 0){std::cout << "mod zero, abort" << std::endl;result = -1;}else{result = elemOne_ % elemTwo_;}}break;default:std::cout << "非法操作: " << operator_ << std::endl;break;}return result;}int get(int *e1, int *e2, char *op){*e1 = elemOne_;*e2 = elemTwo_;*op = operator_;}
private:int elemOne_;int elemTwo_;char operator_;
};

ThreadPool.hpp

#pragma once#include <iostream>
#include <cassert>
#include <queue>
#include <memory>
#include <cstdlib>
#include <pthread.h>
#include <unistd.h>
#include <sys/prctl.h>
#include "Log.hpp"
#include "Lock.hpp"using namespace std;int gThreadNum = 5;template <class T>
class ThreadPool
{
private:ThreadPool(int threadNum = gThreadNum) : threadNum_(threadNum), isStart_(false){assert(threadNum_ > 0);pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}ThreadPool(const ThreadPool<T> &) = delete;void operator=(const ThreadPool<T>&) = delete;public:static ThreadPool<T> *getInstance(){static Mutex mutex;if (nullptr == instance) //仅仅是过滤重复的判断{LockGuard lockguard(&mutex); //进入代码块,加锁。退出代码块,自动解锁if (nullptr == instance){instance = new ThreadPool<T>();}}return instance;}//类内成员, 成员函数,都有默认参数thisstatic void *threadRoutine(void *args){pthread_detach(pthread_self());ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);prctl(PR_SET_NAME, "follower");while (1){tp->lockQueue();while (!tp->haveTask()){tp->waitForTask();}//这个任务就被拿到了线程的上下文中T t = tp->pop();tp->unlockQueue();// for debugint one, two;char oper;t.get(&one, &two, &oper);//规定,所有的任务都必须有一个run方法Log() << "新线程完成计算任务: " << one << oper << two << "=" << t.run() << "\n";}}void start(){assert(!isStart_);for (int i = 0; i < threadNum_; i++){pthread_t temp;pthread_create(&temp, nullptr, threadRoutine, this);}isStart_ = true;}void push(const T &in){lockQueue();taskQueue_.push(in);choiceThreadForHandler();unlockQueue();}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}private:void lockQueue() { pthread_mutex_lock(&mutex_); }void unlockQueue() { pthread_mutex_unlock(&mutex_); }bool haveTask() { return !taskQueue_.empty(); }void waitForTask() { pthread_cond_wait(&cond_, &mutex_); }void choiceThreadForHandler() { pthread_cond_signal(&cond_); }T pop(){T temp = taskQueue_.front();taskQueue_.pop();return temp;}private:bool isStart_;int threadNum_;queue<T> taskQueue_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *instance;// const static int a = 100;
};template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;

ThreadPoolTest.cc

#include "ThreadPool.hpp"
#include "Task.hpp"
#include <ctime>
#include <thread>// 如何对一个线程进行封装, 线程需要一个回调函数,支持lambda
// class tread{
// };int main()
{prctl(PR_SET_NAME, "master");const string operators = "+/*/%";// unique_ptr<ThreadPool<Task> > tp(new ThreadPool<Task>());unique_ptr<ThreadPool<Task> > tp(ThreadPool<Task>::getInstance());tp->start();srand((unsigned long)time(nullptr) ^ getpid() ^ pthread_self());// 派发任务的线程while(true){int one = rand()%50;int two = rand()%10;char oper = operators[rand()%operators.size()];Log() << "主线程派发计算任务: " << one << oper << two << "=?" << "\n";Task t(one, two, oper);tp->push(t);sleep(1);}
}

相关文章:

生产者消费者模型线程池(纯代码)

目录 生产者消费者模型 条件变量&&互斥锁&#xff08;阻塞队列&#xff09; makefile Task.hpp BlockQueue.hpp BlockQueueTest.cc 信号量&&互斥锁&#xff08;环形队列&#xff09; makefile RingQueue.hpp RingQueueTest.cc 线程池&#xff08;封…...

K8s 应用的网络可观测性: Cilium VS DeepFlow

随着分布式服务架构的流行,特别是微服务等设计理念在现代应用普及开来,应用中的服务变得越来越分散,因此服务之间的通信变得越来越依赖网络,很有必要来谈谈实现微服务可观测性中越来越重要的一环——云原生网络的可观测。K8s 是微服务设计理念能落地的最重要的承载体,本文…...

3.29面试题

文章目录内存内存管理执行过程要点面试题内存 内存管理 由JVM管理 堆&#xff1a;new出来的对象&#xff08;包括成员变量、数组元素、方法的地址&#xff09;栈&#xff1a;局部变量&#xff08;包括方法的参数&#xff09;方法区&#xff1a;.class字节码文件&#xff08;…...

操作系统漏洞发现

操作系统漏洞发现前言一、操作系统漏洞发现1.1 namp2. Goby3. Nessus二&#xff0c;进行渗透测试2.1 使用工具进行渗透1. metasploit2.2 EXP2.3 复现文章三&#xff0c;操作系统漏洞修复前言 不管是对于App来说&#xff0c;还是web站点来说&#xff0c;操作系统是必须的&#x…...

Linux gdb调试底层原理

TOC 前言 linux下gdb调试程序操作过程参考本人文章:gdb调试操作; 这里不再叙述; 本文主要内容是介绍GDB本地调试的底层调试原理&#xff0c;我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序; 总结部分是断点调试的底层原理&#xff0c;可以直接跳转过去先看看大概…...

LC-1647. 字符频次唯一的最小删除次数(哈希+计数)

1647. 字符频次唯一的最小删除次数 难度中等56 如果字符串 s 中 不存在 两个不同字符 频次 相同的情况&#xff0c;就称 s 是 优质字符串 。 给你一个字符串 s&#xff0c;返回使 s 成为 优质字符串 需要删除的 最小 字符数。 字符串中字符的 频次 是该字符在字符串中的出现…...

HTTP状态码

100: 接受&#xff0c;正在继续处理 200: 请求成功&#xff0c;并返回数据 201: 请求已创建 202: 请求已接受 203: 请求成为&#xff0c;但未授权 204: 请求成功&#xff0c;没有内容 205: 请求成功&#xff0c;重置内容 206: 请求成功&#xff0c;返回部分内容 301: 永久性重定…...

【Linux】初见“which命令”,“find命令”以及linux执行命令优先级

文章目录1.which命令1.1 whereis命令1.2 locate命令1.3 搜索文件命令总结2.find命令2.1 find之exec用法2.2 管道符之xargs用法3 Linux常用命令4.命令执行优先级1.which命令 查找命令文件存放目录 搜索范围由环境变量PATH决定&#xff08;echo $PATH) which命令格式&#xff1…...

update case when 多字段,多条件, mysql中case when用法

文章目录 前言 sql示例 普通写法&#xff1a; update case when写法 update case when 多字段写法 case when语法 case when 的坑 1、不符合case when条件但是字段被更新为null了 解决方法一&#xff1a;添加where条件 解决方法二&#xff1a;添加else 原样输出 2、同一条数据符…...

mysql隐式转换 “undefined“字符串匹配到mysql int类型0值字段

描述&#xff1a;mysql 用字符串搜索 能搜到int类型查询结果 mysql int类型条件用字符串查询 table: CREATE TABLE all_participate_records (id bigint unsigned NOT NULL AUTO_INCREMENT,created_at datetime(3) DEFAULT NULL,updated_at datetime(3) DEFAULT NULL,deleted…...

Redis八股文

1.Redis是什么? Redis 是一个基于 C 语言开发的开源数据库&#xff08;BSD 许可&#xff09;&#xff0c;与传统数据库不同的是 Redis 的数据是存在内存中的&#xff08;内存数据库&#xff09;&#xff0c;读写速度非常快&#xff0c;被广泛应用于缓存方向。并且&#xff0c…...

InnoDB——详细解释锁的应用,一致性读,自增长与外键

一致性非锁定读 一致性的非锁定读&#xff08;consistent nonlocking read&#xff09;是指InnoDB存储引擎通过行多版本控制的方式读取当前执行时数据库中行的数据。 如果读取的行正在执行 行Delete或Update操作&#xff0c;这时读取操作不会因此去等待行上锁的释放。相反&…...

C++模板基础(四)

函数模板&#xff08;四&#xff09; ● 函数模板的实例化控制 – 显式实例化定义&#xff1a; template void fun(int) / template void fun(int) //header.h template<typename T> void fun(T x) {std::cout << x << std::endl; }//main.cpp #include&quo…...

pycharm使用记录

文章目录下载安装后续其他设置编辑器设置关于debug下载安装 直接去pycharm官网下载社区版&#xff0c;这个版本本来就是免费的&#xff0c;而且功能其实已经够了 后续其他设置 首先&#xff0c;第一次启动时&#xff0c;记得在preference->interpreter中设置python环境&a…...

Linux命令·kill·killall

Linux中的kill命令用来终止指定的进程&#xff08;terminate a process&#xff09;的运行&#xff0c;是Linux下进程管理的常用命令。通常&#xff0c;终止一个前台进程可以使用CtrlC键&#xff0c;但是&#xff0c;对于一个后台进程就须用kill命令来终止&#xff0c;我们就需…...

Linux /proc/version 文件解析

/proc/version文件里面的内容: Linux version 4.14.180-perf (oe-user@oe-host) (clang version 10.0.5 for Android NDK, GNU ld (GNU Binutils) 2.29.1.20180115) #1 SMP PREEMPT Wed Mar 29 18:55:02 CST 2023 /proc/version文件里面记录了如下内容: 1、Linux kernel的…...

【Django 网页Web开发】15. 实战项目:管理员增删改查,md5密码和密码重置(08)(保姆级图文)

目录1. model编写数据表2. 管理员列表2.1 admin.py视图文件2.2 admin_list.html2.3 url.py2.4 最终效果3. 管理员添加3.0 md5包的书写3.1 form.py表单组件3.2 admin.py视图文件3.3 引入公共的添加数据html3.4 url.py3.5 最终效果4. 管理员编辑4.0 form表单组件4.1 admin.py视图…...

STL容器之<array>

文章目录测试环境array介绍头文件模块类定义对象构造初始化元素访问容器大小迭代器其他函数测试环境 系统&#xff1a;ubuntu 22.04.2 LTS 64位 gcc版本&#xff1a;11.3.0 编辑器&#xff1a;vsCode 1.76.2 array介绍 array是固定大小的序列式容器&#xff0c;它包含按严格…...

flask教程6:cookie和session

文章目录一、cookie1.1 什么是cookie&#xff1f;1.2 使用cookie1.2.1 设置cookie1.2.2设置cookie的有效期1.2.3在Flask中查询cookie1.2.4删除cookie二、session2.1实现session的两种思路2.1.1 第一种2.1.2 第二种2.2使用session2.2 .1设置session2.2.2 设置有效期2.2.3 获取se…...

【JavaEE初阶】第六节.网络原理TCP/IP协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 一、TCP/IP协议五层协议栈&#xff1b; 1.1 应用层协议&#xff1b; 二、传输层协议&#xff1b; 2.1 UDP协议&#xff1b; 2.2 TCP协议&#xff1b; 2.…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

在rocky linux 9.5上在线安装 docker

前面是指南&#xff0c;后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...