C++线程池手写实现

1.Thread类的封装

封装Thread类,使其可以直接在外部调用对象的start,detach,join和cancel等方法来实现对线程的操作

1.1代码

//Thread.h//
// Created by crab on 2024/10/20.
//#ifndef THREAD_H
#define THREAD_H#include <pthread.h>class Thread {
public:virtual ~Thread();bool start(void *arg);bool detach();bool join();bool cancel();[[nodiscard]] pthread_t getThreadId() const;protected:Thread();//在派生类中实现virtual void run(void *arg) = 0;private:static void *threadRun(void *);private:void *mArg;bool mIsStart;bool mIsDetach;pthread_t mThreadId{};
};
#endif //THREAD_H
//Thread.cpp
//
// Created by crab on 2024/10/20.
//#include "Thread.h"Thread::Thread() :mArg(NULL),mIsStart(false),mIsDetach(false)
{/*初始化类的成员变量线程函数的参数为NULL, 线程启动状态和分离状态均设置为false*/
}Thread::~Thread() {//检查线程是否已经启动且也没有分离,如果是,调用detach来分离线程,避免对象销毁后产生资源泄露if(mIsStart == true && mIsDetach == false)detach();
}bool Thread::start(void *arg) {//用于启动线程mArg = arg;//创建一个新线程,线程的入口函数为静态成员函数threadRun,并将当前Thread对象作为参数传递给线程,如果失败,返回falseif(pthread_create(&mThreadId, NULL, threadRun, this))return false;//标记线程启动mIsStart = true;return true;
}bool Thread::detach() {//如果线程没有启动,返回falseif(mIsStart != true)return false;//如果线程已经分离,返回trueif(mIsDetach == true)return true;//调用pthread库函数将线程设置为分离,成功则标记IsDetach为true,失败返回falseif(pthread_detach(mThreadId))return false;mIsDetach = true;return true;}bool Thread::join() {//等待线程结束//如果没有启动或者已经分离,则返回falseif(mIsStart != true || mIsDetach == true)return false;//如果join失败,返回false,否则返回trueif(pthread_join(mThreadId, NULL))return false;return true;
}bool Thread::cancel() {//用于线程取消//如果线程没启动,返回falseif(mIsStart != true)return false;//取消成功mIsStart设置为false,返回true, 失败则返回falseif(pthread_cancel(mThreadId))return false;mIsStart = false;return true;
}pthread_t Thread::getThreadId() const {//用于获取线程的描述符return mThreadId;
}void *Thread::threadRun(void *arg) {//作为线程的入口点 arg时指向对象Thread的指针,调用派生类中实现的run()方法,来执行具体的线程任务Thread* thread = (Thread*)arg;thread->run(thread->mArg);return NULL;}

1.2代码解释

1.2.1类参数
void *mArg;

用于获取参数列表

bool mIsStart;
bool mIsDetach;

布尔变量,用于判断线程目前的状态

pthread_t mThreadId{};

线程ID列表,用来操作对应id的线程

1.2.2类方法
Thread();
virtual ~Thread();Thread::Thread() :mArg(NULL),mIsStart(false),mIsDetach(false)
{/*初始化类的成员变量线程函数的参数为NULL, 线程启动状态和分离状态均设置为false*/
}Thread::~Thread() {//检查线程是否已经启动且也没有分离,如果是,调用detach来分离线程,避免对象销毁后产生资源泄露if(mIsStart == true && mIsDetach == false)detach();
}

构造函数和析构函数,其中析构函数是虚函数,可以在派生类中重写析构函数。构造函数中将mArg,mIsStart,mIsDetach三个变量初始化。析构函数中检查线程状态,如果已经开始但未分离的线程先将其分离,避免对象销毁后产生资源泄露。

bool start(void *arg);
bool Thread::start(void *arg) {//用于启动线程mArg = arg;//创建一个新线程,线程的入口函数为静态成员函数threadRun,并将当前Thread对象作为参数传递给线程,如果失败,返回falseif(pthread_create(&mThreadId, NULL, threadRun, this))return false;//标记线程启动mIsStart = true;return true;
}

start函数用于启动线程,利用pthread_create创建一个线程,失败返回false,成功则将启动标识符mIsStart置为true,并返回true

bool detach();
bool Thread::detach() {//如果线程没有启动,返回falseif(mIsStart != true)return false;//如果线程已经分离,返回trueif(mIsDetach == true)return true;//调用pthread库函数将线程设置为分离,成功则标记IsDetach为true,失败返回falseif(pthread_detach(mThreadId))return false;mIsDetach = true;return true;
}

detach操作函数,进行两次条件检查,如果线程没有启动或已经分离(mIsStart == true或者mIsDetach == False),则不进行操作,通过检查后,利用phread_detach来分离线程,并且将mIsDetach置为true。

bool join();
bool Thread::join() {//等待线程结束//如果没有启动或者已经分离,则返回falseif(mIsStart != true || mIsDetach == true)return false;//如果join失败,返回false,否则返回trueif(pthread_join(mThreadId, NULL))return false;return true;
}

join操作,用于等待线程运行结束,检查线程是否尚未启动或已经分离,如果是则返回false,否则join对应的线程。

bool cancel();
bool Thread::cancel() {//用于线程取消//如果线程没启动,返回falseif(mIsStart != true)return false;//取消成功mIsStart设置为false,返回true, 失败则返回falseif(pthread_cancel(mThreadId))return false;mIsStart = false;return true;
}

cancel函数,用于线程取消,逻辑与上面类似

[[nodiscard]] pthread_t getThreadId() const;
pthread_t Thread::getThreadId() const {//用于获取线程的描述符return mThreadId;
}

用于获取线程的描述符,必须接收返回值

void *Thread::threadRun(void *arg) {//作为线程的入口点 arg时指向对象Thread的指针,调用派生类中实现的run()方法,来执行具体的线程任务Thread* thread = (Thread*)arg;thread->run(thread->mArg);return NULL;
}

线程的入口点,通过调用派生类中的run方法来实现利用线程完成任务

virtual void run(void *arg) = 0;

纯虚函数,在派生类中实现,作为线程的任务

1.3测试

//测试代码和main函数
class MyThread : public Thread {
public:MyThread(Mutex &mutex, int &sharedResource): mMutex(mutex), mSharedResource(sharedResource) {}protected:void run(void *arg) override {// 使用 Mutex 进行同步pthread_mutex_lock(mMutex.get());std::cout << "Thread " << getThreadId() << " started." << std::endl;// 增加共享资源mSharedResource++;std::cout << "Shared resource value: " << mSharedResource << std::endl;std::cout << "Thread " << getThreadId() << " finished." << std::endl;pthread_mutex_unlock(mMutex.get());}private:Mutex &mMutex;int &mSharedResource;
};int main() {Mutex mutex;int sharedResource = 0;// 创建并启动多个线程MyThread *threads[30];for (int i = 0; i < 30; ++i) {threads[i] = new MyThread(mutex, sharedResource);threads[i]->start(nullptr);}// 等待所有线程完成for (int i = 0; i < 30; ++i) {threads[i]->join();}for (int i = 0; i < 30; ++i) {delete threads[i];}std::cout << "Final shared resource value: " << sharedResource << std::endl;return 0;
}

运行结果
tmp/tmp.NkOftkIsac/cmake-build-debug-remote-host2/ServerStudy
Thread 140583459776256 started.
Shared resource value: 1
Thread 140583459776256 finished.
Thread 140583434598144 started.
Shared resource value: 2
Thread 140583434598144 finished.
Thread 140583442990848 started.
Shared resource value: 3
Thread 140583442990848 finished.
Thread 140583451383552 started.
Shared resource value: 4
Thread 140583451383552 finished.
Thread 140583426205440 started.
Shared resource value: 5
Thread 140583426205440 finished.

2.ThreadRegister

因为在编程中,一般任务也就是我们要运行的函数,这里实现一个ThreadRegister,来激活一个线程并且绑定对应的任务。

2.1 代码

//ThreadRegister.h
//
// Created by crab on 2024/10/22.
//#ifndef THREADREGISTER_H
#define THREADREGISTER_H
#include "Thread.h"#include <functional>
#include <tuple>
#include <utility>class ThreadRegister : public Thread {
public:// 使用std::function和std::tuple表示任务和其参数template<typename Func, typename... Args>explicit ThreadRegister(Func&& func, Args&&... args);protected:void run(void *arg) override;private:std::function<void()> mTask; // 存储任务
};template<typename Func, typename... Args>
ThreadRegister::ThreadRegister(Func&& func, Args&&... args) {// 使用std::bind和std::forward将任务和参数绑定,这样mTask被调用时会自动调用func并传递参数argsmTask = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
}inline void ThreadRegister::run(void *arg) {if (mTask) {mTask(); // 执行绑定的任务}
}#endif //THREADREGISTER_H

在ThreadRegister类中,主要实现两个函数,一个是利用bind和模板来将任务和参数绑定为一个可调用对象,这里的func即为要绑定的任务,args为任务需要的参数列表,第二个是重写基类Thread中的run函数,来执行绑定的任务。

3.Mutex类

Mutex类中封装了pthread_mutex,对外提供lock,tryLock,unlock和get三种方法

3.1 代码

//Mutex.h
//
// Created by crab on 2024/10/21.
//#ifndef MUTEX_H
#define MUTEX_H
#include "Thread.h"
#include <mutex>class Mutex {
public:Mutex();~Mutex();Mutex(const Mutex&) = delete;Mutex& operator=(const Mutex&) = delete;//加锁void lock();//尝试加锁,非阻塞bool tryLock();//解锁void unlock();//获取pthread_mutex_t指针pthread_mutex_t* get() {return &mMutex;}private:pthread_mutex_t mMutex{};
};#endif //MUTEX_H
//Mutex.cpp
//
// Created by crab on 2024/10/21.
//#include "Mutex.h"#include <pthread.h>Mutex::Mutex() {pthread_mutex_init(&mMutex, nullptr);
}Mutex::~Mutex() {pthread_mutex_destroy(&mMutex);
}void Mutex::lock() {pthread_mutex_lock(&mMutex);
}bool Mutex::tryLock() {return pthread_mutex_trylock(&mMutex) == 0;
}void Mutex::unlock() {pthread_mutex_unlock(&mMutex);
}

3.2 类的实现

这里代码比较简单,就不单独解释了。
在类定义中,Mutex封装类主要包括四个方法,加锁,尝试加锁,解锁以及获取对象的pthread_mutex_t指针,在前三种方法中,只需要封装其在pthread_mutex中的对应函数方法即可。

3.ThreadPool

3.1前言

上面的几个类是用来实现线程池的封装类,让我们能够更简单的调用需要的方法来实现目标,这里终于写到了怎么实现线程池,大概分几个部分,线程池原理,线程池实现代码,线程池代码讲解,线程池的测试这几个。

3.2线程池原理

线程池,顾名思义就是一个用来调用线程的池子,我们往池子内投入任务,池子自动调用线程然后帮助我们完成任务,之后销毁线程,因此,其实可以把线程池看作一个简单的线程管理工具,而这个管理工具对外封闭,仅仅向用户开放一个“投入任务”的接口。
在这里插入图片描述
借用C/C++手撕线程池(线程池的封装和实现)的图来表现线程池的任务方法

基本上线程池就可以分为以下几个部分

从数据结构上来说,需要两个队列:
1.工作队列:用来存储线程池中的工作线程

std::vector<ThreadRegister*> mWorkers; //工作线程队列

2.任务队列:用来储存需要完成的任务,也就是绑定的函数和参数列表

std::queue<std::function<void()>> mTasks; //任务队列

以及几个用来控制的变量

Mutex mQueueMutex; //保护任务队列的互斥锁
std::condition_variable mCondition; //条件变量
std::atomic<bool> mStop; //停止标志
size_t mMaxTasks; //最大任务数

从类方法上来说,需要构造,析构,添加任务以及完成任务这四个函数,其中添加任务以及构造函数为对外暴露的函数,提供给用户来操作。

ThreadPool(size_t threadCount, size_t maxTasks);
~ThreadPool();template<typename Func, typename... Args>
bool addTask(Func& func, Args&&... args);void workerTask();

3.3 实现

//
// Created by crab on 2024/10/22.
////ThreadPool.h#ifndef THREADPOOL_H
#define THREADPOOL_H
#include <atomic>
#include <condition_variable>
#include <iostream>#include "ThreadRegister.h"
#include "Mutex.h"#include <vector>
#include <queue>class ThreadPool {
public:ThreadPool(size_t threadCount, size_t maxTasks);~ThreadPool();//添加任务template<typename Func, typename... Args>bool addTask(Func& func, Args&&... args);//停止线程池void stop();//调整线程数void resizeThread(size_t newThreadCount);private://工作线程函数void workerTask();std::vector<ThreadRegister*> mWorkers; //工作线程队列std::queue<std::function<void()>> mTasks; //任务队列Mutex mQueueMutex; //保护任务队列的互斥锁std::condition_variable mCondition; //条件变量,用来实现线程同步std::atomic<bool> mStop; //停止标志size_t mMaxTasks; //最大任务数};template<typename Func, typename... Args>
bool ThreadPool::addTask(Func &func, Args &&... args) {{mQueueMutex.lock();if (mTasks.size() >= mMaxTasks) {std::cerr << "Task queue is full. Cannot add more tasks." << std::endl;mQueueMutex.unlock();return false;}// 添加任务mTasks.emplace(std::bind(std::forward<Func>(func), std::forward<Args>(args)...));mQueueMutex.unlock();}mCondition.notify_one(); // 通知一个空闲线程return true;
}#endif //THREADPOOL_H
//
// Created by crab on 2024/10/22.
////ThreadPool.cpp#include "ThreadPool.h"
#include <iostream>
#include <thread>ThreadPool::ThreadPool(size_t threadCount, size_t maxTasks): mStop(false), mMaxTasks(maxTasks)
{if (threadCount < 1 || maxTasks < 1) {std::cerr <<"workers num error"<<std::endl;return;}for(size_t i = 0; i < threadCount; i++) {//使用ThreadRegister创建线程实例,将workerTask作为任务auto* worker = new ThreadRegister([this]{this->workerTask();});//启动对应的线程实例worker -> start(nullptr);//讲线程添加至mWorkers中mWorkers.push_back(worker);}
}ThreadPool::~ThreadPool() {{// 加锁mQueueMutex.lock();mStop = true;mQueueMutex.unlock();}mCondition.notify_all();// 等待所有工作线程完成for (ThreadRegister* worker : mWorkers) {worker->join();delete worker; // 释放动态分配的 ThreadRegister}
}void ThreadPool::workerTask() {while (true) {std::function<void()> task;{// 手动加锁mQueueMutex.lock();// 检查停止标志和任务队列if (mStop && mTasks.empty()) {mQueueMutex.unlock(); // 手动解锁return; // 线程退出}// 如果有任务,则获取任务if (!mTasks.empty()) {task = std::move(mTasks.front());mTasks.pop();}mQueueMutex.unlock(); // 手动解锁}if (task) {task();}else {//避免忙等待std::this_thread::sleep_for(std::chrono::milliseconds(10));}}
}void ThreadPool::stop() {{mQueueMutex.lock();mStop = true;mQueueMutex.unlock();}mCondition.notify_all(); // 通知所有线程退出// 等待所有工作线程完成for (ThreadRegister* worker : mWorkers) {worker->join();delete worker;}mWorkers.clear(); // 清空线程池
}void ThreadPool::resizeThread(size_t newThreadCount) {if (newThreadCount == mWorkers.size()) return;// 增加新线程if (newThreadCount > mWorkers.size()) {size_t threadsToAdd = newThreadCount - mWorkers.size();for (size_t i = 0; i < threadsToAdd; ++i) {auto* worker = new ThreadRegister([this]{ this->workerTask(); });worker->start(nullptr);mWorkers.push_back(worker);}}// 减少线程else {size_t threadsToRemove = mWorkers.size() - newThreadCount;for (size_t i = 0; i < threadsToRemove; ++i) {mQueueMutex.lock();mStop = true;mQueueMutex.unlock();mCondition.notify_one(); // 通知一个线程退出mWorkers.back()->join();delete mWorkers.back();mWorkers.pop_back();}}
}
3.3.1 构造函数
ThreadPool::ThreadPool(size_t threadCount, size_t maxTasks): mStop(false), mMaxTasks(maxTasks)
{if (threadCount < 1 || maxTasks < 1) {std::cerr <<"workers num error"<<std::endl;return;}for(size_t i = 0; i < threadCount; i++) {//使用ThreadRegister创建线程实例,将workerTask作为任务auto* worker = new ThreadRegister([this]{this->workerTask();});//启动对应的线程实例worker -> start(nullptr);//讲线程添加至mWorkers中mWorkers.push_back(worker);}
}

首先判断构造是否合规,然后根据工作线程数threadCount来使用ThreadRegister注册封装好的工作线程,并且将线程添加到vector容器中,构造工作队列。

3.3.2析构函数
ThreadPool::~ThreadPool() {{// 加锁mQueueMutex.lock();mStop = true;mQueueMutex.unlock();}mCondition.notify_all();// 等待所有工作线程完成for (ThreadRegister* worker : mWorkers) {worker->join();delete worker; // 释放动态分配的 ThreadRegister}
}

首先将mStop置为true,然后等待所有工作线程完成,释放动态分配的内存

3.3.3 addTask
template<typename Func, typename... Args>
bool ThreadPool::addTask(Func &func, Args &&... args) {{mQueueMutex.lock();if (mTasks.size() >= mMaxTasks) {std::cerr << "Task queue is full. Cannot add more tasks." << std::endl;mQueueMutex.unlock();return false;}// 添加任务mTasks.emplace(std::bind(std::forward<Func>(func), std::forward<Args>(args)...));mQueueMutex.unlock();}mCondition.notify_one(); // 通知一个空闲线程return true;
}

首先加锁,判断添加任务是否合规(是否大于最大可接受任务数),然年后在mTasks尾构造新的任务,并且通知一个空闲线程。

注意:因为这个函数是模板函数,他的实例化是在编译阶段,因此需要在.h文件中定义可见

3.3.4 workerTask
void ThreadPool::workerTask() {while (true) {std::function<void()> task;{// 手动加锁mQueueMutex.lock();// 检查停止标志和任务队列if (mStop && mTasks.empty()) {mQueueMutex.unlock(); // 手动解锁return; // 线程退出}// 如果有任务,则获取任务if (!mTasks.empty()) {task = std::move(mTasks.front());mTasks.pop();}mQueueMutex.unlock(); // 手动解锁}if (task) {task();}else {//避免忙等待std::this_thread::sleep_for(std::chrono::milliseconds(10));}}
}

加锁,然后检查停止标志以任务队列是否为空,如果任务队列不为空,则获取队首任务,并且判断该任务是否为空,如果不为空则执行,否则线程sleep10毫秒来避免忙等待。

3.3.5 stop和resizeThread

两个功能性函数,分别是强制停止线程以及改变线程池内的工作线程数。

void ThreadPool::stop() {{mQueueMutex.lock();mStop = true;mQueueMutex.unlock();}mCondition.notify_all(); // 通知所有线程退出// 等待所有工作线程完成for (ThreadRegister* worker : mWorkers) {worker->join();delete worker;}mWorkers.clear(); // 清空线程池
}void ThreadPool::resizeThread(size_t newThreadCount) {if (newThreadCount == mWorkers.size()) return;// 增加新线程if (newThreadCount > mWorkers.size()) {size_t threadsToAdd = newThreadCount - mWorkers.size();for (size_t i = 0; i < threadsToAdd; ++i) {auto* worker = new ThreadRegister([this]{ this->workerTask(); });worker->start(nullptr);mWorkers.push_back(worker);}}// 减少线程else {size_t threadsToRemove = mWorkers.size() - newThreadCount;for (size_t i = 0; i < threadsToRemove; ++i) {mQueueMutex.lock();mStop = true;mQueueMutex.unlock();mCondition.notify_one(); // 通知一个线程退出mWorkers.back()->join();delete mWorkers.back();mWorkers.pop_back();}}
}

3.4 测试


void task(int taskid) {std::cout<<"now thread task "<< taskid <<" is runing....."<<std::endl;}int main() {ThreadPool pool(10, 100);unsigned int cores = std::thread::hardware_concurrency();std::cout << "Number of CPU cores: " << cores << std::endl;for (int i=0; i<100; i++) {bool flag = pool.addTask(task, i);}return 0;
}

/tmp/tmp.NkOftkIsac/cmake-build-debug-remote-host2/ServerStudy
Number of CPU cores: 2
now thread task 0 is runing…
now thread task 1 is runing…
now thread task 2 is runing…
now thread task 3 is runing…
now thread task 4 is runing…
now thread task 5 is runing…
now thread task 6 is runing…
now thread task 7 is runing…
now thread task 8 is runing…
now thread task 9 is runing…
now thread task 10 is runing…
now thread task 11 is runing…
now thread task 12 is runing…
now thread task 13 is runing…
now thread task 14 is runing…
now thread task 15 is runing…
now thread task 16 is runing…
now thread task 17 is runing…
now thread task 18 is runing…
now thread task 19 is runing…
now thread task 20 is runing…
now thread task 21 is runing…
now thread task 22 is runing…
now thread task 23 is runing…
now thread task 24 is runing…
now thread task 25 is runing…
now thread task 26 is runing…
now thread task 27 is runing…
now thread task 28 is runing…
now thread task 29 is runing…
now thread task 30 is runing…
now thread task 31 is runing…
now thread task 32 is runing…
now thread task 33 is runing…
now thread task 34 is runing…
now thread task 35 is runing…
now thread task 36 is runing…
now thread task 37 is runing…
now thread task 38 is runing…
now thread task 39 is runing…
now thread task 40 is runing…
now thread task 41 is runing…
now thread task 42 is runing…
now thread task 43 is runing…
now thread task 44 is runing…
now thread task 45 is runing…
now thread task 46 is runing…
now thread task 47 is runing…
now thread task 48 is runing…
now thread task 49 is runing…
now thread task 50 is runing…
now thread task 51 is runing…
now thread task 52 is runing…
now thread task 53 is runing…
now thread task 54 is runing…
now thread task 55 is runing…
now thread task 56 is runing…
now thread task 57 is runing…
now thread task 58 is runing…
now thread task 59 is runing…
now thread task 60 is runing…
now thread task 61 is runing…
now thread task 62 is runing…
now thread task 63 is runing…
now thread task 64 is runing…
now thread task 65 is runing…
now thread task 66 is runing…
now thread task 67 is runing…
now thread task 68 is runing…
now thread task 69 is runing…
now thread task 70 is runing…
now thread task 71 is runing…
now thread task 72 is runing…
now thread task 73 is runing…
now thread task 74 is runing…
now thread task 75 is runing…
now thread task 76 is runing…
now thread task 77 is runing…
now thread task 78 is runing…
now thread task 79 is runing…
now thread task 80 is runing…
now thread task 81 is runing…
now thread task 82 is runing…
now thread task 83 is runing…
now thread task 84 is runing…
now thread task 85 is runing…
now thread task 86 is runing…
now thread task 87 is runing…
now thread task 88 is runing…
now thread task 89 is runing…
now thread task 90 is runing…
now thread task 91 is runing…
now thread task 92 is runing…
now thread task 93 is runing…
now thread task 94 is runing…
now thread task 95 is runing…
now thread task 96 is runing…
now thread task 97 is runing…
now thread task 98 is runing…
now thread task 99 is runing…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/458040.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Docker 管理完整项目:Java、Vue、Redis 和 Nginx 的一站式部署

个人名片 🎓作者简介:java领域优质创作者 🌐个人主页:码农阿豪 📞工作室:新空间代码工作室(提供各种软件服务) 💌个人邮箱:[2435024119@qq.com] 📱个人微信:15279484656 🌐个人导航网站:www.forff.top 💡座右铭:总有人要赢。为什么不能是我呢? 专栏导…

[大模型学习推理]资料

https://juejin.cn/post/7353963878541361192 lancedb是个不错的数据库&#xff0c;有很多学习资料 https://github.com/lancedb/vectordb-recipes/tree/main/tutorials/Multi-Head-RAG-from-Scratch 博主讲了很多讲解&#xff0c;可以参考 https://juejin.cn/post/7362789…

js纯操作dom版购物车(实现购物车功能)

代码&#xff1a; <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document</title>&l…

Linux 进程间通信_匿名管道

1.程间通信目的 : 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享同样的资源。 通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff08;如…

vue3+vite 部署npm 包

公司需要所以研究了一下怎么部署安装&#xff0c;比较简单 先下载个vue项目 用vue脚手架安装vue3项目 删掉App.vue 中的其它组件 npm create vuelatest 开始写自定义组件 新建一个el-text 组件, name是重点&#xff0c;vue3中也得这么导出name属性 <script> export de…

JAVA-----线程池

目录 1.简单理解一下线程池 2.线程池参数 3.Executors ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 1.简单理解一下线程池 线程池&#xff0c;就是用来存储线程的&#xff0c;然后如果有任务传进线程池…

PortQry下载安装使用教程(超详细),Windows测试UDP端口

《网络安全自学教程》 PortQry是微软官方提供的一款TCP/IP连接「排障工具」&#xff0c;用来「检查」TCP/UDP「端口状态」。 平时检查端口状态&#xff0c;最常用的是telnet&#xff0c;但它是基于TCP协议的&#xff0c;无法检测「UDP端口」&#xff0c;这篇文章教大家如何在W…

Flink-cdc Schema Evolution 详解

Flink-cdc Schema Evolution 详解 github原文 glimpse flink-cdc-3 glimpse 源码基于 ~/project/flink_src/flink-cdc master !4 ❯ git remote -v origin https://github.com/apache/flink-cdc.git (fetch) origin https://github.com/apache/flink-cdc.git (push) ~/p…

介绍一款Java开发的企业接口管理系统和开放平台

介绍一款Java开发的企业接口管理系统和开放平台&#xff0c;YesApi接口管理平台Java版本。该系统基于Java开发&#xff0c;可以帮助企业进行统一接口管理、API接口开放&#xff0c;以及用于PaaS系统和SaaS产品平台的快速二次开发和搭建。 一、系统概述 YesApi接口大师&#x…

ClickHouse 5节点集群安装

ClickHouse 5节点集群安装 在此架构中&#xff0c;配置了五台服务器。其中两个用于托管数据副本。其他三台服务器用于协调数据的复制。在此示例中&#xff0c;我们将创建一个数据库和表&#xff0c;将使用 ReplicatedMergeTree 表引擎在两个数据节点之间复制该数据库和表。 官…

RHCE作业二

1.要求&#xff1a; 配置nginx服务通过ip访问多网站 2. 1关闭防火墙 2创建ip 3配置 4创建文件 5测试

logback 如何将日志输出到文件

如何作 将日志输出到文件需要使用 RollingFileAppender&#xff0c;该 Appender 必须定义 rollingPolicy &#xff0c;另外 rollingPollicy 下必须定义 fileNamePattern 和 encoder <appender name"fileAppender" class"ch.qos.logback.core.rolling.Rollin…

二、Spring的执行流程

文章目录 1. spring的初始化过程1.1 ClassPathXmlApplicationContext的构造方法1.2 refresh方法&#xff08;核心流程&#xff09;1.2.1 prepareRefresh() 方法1.2.2 obtainFreshBeanFactory() 方法1.2.3 prepareBeanFactory() 方法1.2.4 invokeBeanFactoryPostProcessors() 方…

shodan2---清风

注&#xff1a;本文章源于泷羽SEC&#xff0c;如有侵权请联系我&#xff0c;违规必删 学习请认准泷羽SEC学习视频:https://space.bilibili.com/350329294 实验一&#xff1a;search 存在CVE-2019-0708的网络设备 CVE - 2019 - 0708**漏洞&#xff1a;** 该漏洞存在于远程桌面…

解读数字化转型的敏捷架构:从理论到实践的深度分析

在当今数字经济的推动下&#xff0c;企业要在瞬息万变的市场中保持竞争力&#xff0c;数字化转型已经不再是一种选择&#xff0c;而是不可避免的战略需求。然而&#xff0c;企业如何从理论到实践进行有效的转型&#xff0c;尤其是在复杂的技术环境中&#xff0c;如何通过正确的…

来源爬虫程序调研报告

来源爬虫程序调研报告 一、什么是爬虫 爬虫&#xff1a;就是抓取网页数据的程序。从网站某一个页面&#xff08;通常是首页&#xff09;开始&#xff0c;读取网页的内容&#xff0c;找到在网页中的其它链接地址&#xff0c;然后通过这些链接地址寻找下一个网页&#xff0c;这…

中小型门诊管理系统源码,云诊所管理系统源码,前端技术栈:Vue 2 , Vite , Vue Router 3

中小型门诊管理系统源码&#xff0c;云诊所管理系统源码&#xff0c; 前端技术栈&#xff1a;Vue 2 Vite Vue Router 3 Vuex 3 Element Plus Axios TypeScript Quill Election 后端技术栈&#xff1a;Spring Boot MyBatis MyBatis-Plus Spring Security Swagger2 …

使用Python计算相对强弱指数(RSI)进阶

使用Python计算相对强弱指数&#xff08;RSI&#xff09;进阶 废话不多说&#xff0c;直接上主题&#xff1a;> 代码实现 以下是实现RSI计算的完整代码&#xff1a; # 创建一个DataFramedata {DATE: date_list, # 日期CLOSE: close_px_list, # 收盘价格 }df pd.DataF…

基于丑萌气质狗--C#的sqlserver学习

#region 常用取值 查询List<string> isName new List<string> { "第一", "第二", "第三", "第四" }; List<string> result isName.Where(m > m "第三").ToList();MyDBContext myDBnew MyDBContext(…

【数据分享】中国汽车市场年鉴(2013-2023)

数据介绍 在这十年里&#xff0c;中国自主品牌汽车迅速崛起。吉利、长城、比亚迪等品牌不断推出具有竞争力的车型&#xff0c;在国内市场乃至全球市场都占据了一席之地。同时&#xff0c;新能源汽车的发展更是如日中天。随着环保意识的提高和政策的大力支持&#xff0c;电动汽车…