生产者消费者模型线程池(纯代码)
目录
生产者消费者模型
条件变量&&互斥锁(阻塞队列)
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);} }
相关文章:
生产者消费者模型线程池(纯代码)
目录 生产者消费者模型 条件变量&&互斥锁(阻塞队列) makefile Task.hpp BlockQueue.hpp BlockQueueTest.cc 信号量&&互斥锁(环形队列) makefile RingQueue.hpp RingQueueTest.cc 线程池(封…...
K8s 应用的网络可观测性: Cilium VS DeepFlow
随着分布式服务架构的流行,特别是微服务等设计理念在现代应用普及开来,应用中的服务变得越来越分散,因此服务之间的通信变得越来越依赖网络,很有必要来谈谈实现微服务可观测性中越来越重要的一环——云原生网络的可观测。K8s 是微服务设计理念能落地的最重要的承载体,本文…...
3.29面试题
文章目录内存内存管理执行过程要点面试题内存 内存管理 由JVM管理 堆:new出来的对象(包括成员变量、数组元素、方法的地址)栈:局部变量(包括方法的参数)方法区:.class字节码文件(…...
操作系统漏洞发现
操作系统漏洞发现前言一、操作系统漏洞发现1.1 namp2. Goby3. Nessus二,进行渗透测试2.1 使用工具进行渗透1. metasploit2.2 EXP2.3 复现文章三,操作系统漏洞修复前言 不管是对于App来说,还是web站点来说,操作系统是必须的&#x…...
Linux gdb调试底层原理
TOC 前言 linux下gdb调试程序操作过程参考本人文章:gdb调试操作; 这里不再叙述; 本文主要内容是介绍GDB本地调试的底层调试原理,我们来看一下GDB是通过什么机制来控制被调试程序的执行顺序; 总结部分是断点调试的底层原理,可以直接跳转过去先看看大概…...
LC-1647. 字符频次唯一的最小删除次数(哈希+计数)
1647. 字符频次唯一的最小删除次数 难度中等56 如果字符串 s 中 不存在 两个不同字符 频次 相同的情况,就称 s 是 优质字符串 。 给你一个字符串 s,返回使 s 成为 优质字符串 需要删除的 最小 字符数。 字符串中字符的 频次 是该字符在字符串中的出现…...
HTTP状态码
100: 接受,正在继续处理 200: 请求成功,并返回数据 201: 请求已创建 202: 请求已接受 203: 请求成为,但未授权 204: 请求成功,没有内容 205: 请求成功,重置内容 206: 请求成功,返回部分内容 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决定(echo $PATH) which命令格式࿱…...
update case when 多字段,多条件, mysql中case when用法
文章目录 前言 sql示例 普通写法: update case when写法 update case when 多字段写法 case when语法 case when 的坑 1、不符合case when条件但是字段被更新为null了 解决方法一:添加where条件 解决方法二:添加else 原样输出 2、同一条数据符…...
mysql隐式转换 “undefined“字符串匹配到mysql int类型0值字段
描述: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 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度非常快,被广泛应用于缓存方向。并且,…...
InnoDB——详细解释锁的应用,一致性读,自增长与外键
一致性非锁定读 一致性的非锁定读(consistent nonlocking read)是指InnoDB存储引擎通过行多版本控制的方式读取当前执行时数据库中行的数据。 如果读取的行正在执行 行Delete或Update操作,这时读取操作不会因此去等待行上锁的释放。相反&…...
C++模板基础(四)
函数模板(四) ● 函数模板的实例化控制 – 显式实例化定义: 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官网下载社区版,这个版本本来就是免费的,而且功能其实已经够了 后续其他设置 首先,第一次启动时,记得在preference->interpreter中设置python环境&a…...
Linux命令·kill·killall
Linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令。通常,终止一个前台进程可以使用CtrlC键,但是,对于一个后台进程就须用kill命令来终止,我们就需…...
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介绍头文件模块类定义对象构造初始化元素访问容器大小迭代器其他函数测试环境 系统:ubuntu 22.04.2 LTS 64位 gcc版本:11.3.0 编辑器:vsCode 1.76.2 array介绍 array是固定大小的序列式容器,它包含按严格…...
flask教程6:cookie和session
文章目录一、cookie1.1 什么是cookie?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协议
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一、TCP/IP协议五层协议栈; 1.1 应用层协议; 二、传输层协议; 2.1 UDP协议; 2.2 TCP协议; 2.…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
