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

基于libevent的异步事件驱动型线程池实现

                             +----------------------+|      IFoxThread      |  ← 抽象线程接口|----------------------|| + dispatch()         || + start()            || + stop()             || + ...                |+----------^-----------+|--------------------|----------------------|                                         |+------------------------+              +---------------------------+|       FoxThread        |              |      FoxThreadPool        ||------------------------|              |---------------------------|| 单个线程 + event_loop  |               | 多线程池 + 回调分发机制     || 内部维护 thread/event  |               | 维护线程列表及任务队列      || 支持任务 dispatch     |                | 支持负载均衡分发任务        |+------------------------+              +---------------------------+^                                         ^|                                         ||               +-------------------------+|               |+-----------------------------------+|        FoxThreadManager           ||-----------------------------------||  线程调度管理器,支持按名称查找    ||  维护 map<string, IFoxThread::ptr>||  可分发/广播任务至指定线程或池中   ||  start()/stop() 控制所有子线程     |+-----------------------------------+

threadPool.h

/*** @file threadPool.h* @brief 实现和管理线程池和线程调度* @date 2025-05-11* @copyright Copyright (c) 2025 All rights reserved.*/#ifndef __MYPROJECT_THREAD_POOL_H
#define __MYPROJECT_THREAD_POOL_H#include <thread>
#include <vector>
#include <string>
#include <list>
#include <map>
#include <functional>
#include <memory>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>#include "myproject/singleton.h"
#include "myproject/mutex.h"namespace myproject {class FoxThread;
// 这个 IFoxThread 类是一个线程调度接口类,属于 myproject 框架中的事件线程调度模块,
// 它为不同的线程模型(如单线程、线程池)提供统一的调度方式。IFoxThread 是一个
// 纯虚类,定义了一组线程调度的接口,供其派生类(如 FoxThread、FoxThreadPool)实现。
// IFoxThread 用于抽象线程调度操作,统一事件派发、线程控制和状态输出的接口,
// 让用户无需关心线程模型的具体实现,即可对任务进行分发、广播等操作。
class IFoxThread {
public:typedef std::shared_ptr<IFoxThread> ptr;//被调度的任务typedef std::function<void()> callback;virtual ~IFoxThread() {};//将一个任务分发给某个线程(通常是随机或轮询方式选择)virtual bool dispatch(callback cb);//将任务分发到指定的ID的线程,支持线程定向调度virtual bool dispatch(uint32_t id, callback cb);//批量分发任务,通常用于高性能场景下一次性提交多个任务virtual bool batchDispatch(const std::vector<callback>& cbs);//广播任务,让所有线程都执行一次回调函数,如配置更新、通知等virtual void broadcast(callback cb);//启动线程或线程池virtual void start();//停止线程或线程池virtual void stop();//等待所有线程执行完成,用于程序退出前virtual void join();//输出线程状态到流virtual void dump(std::ostream& os);//返回线程池总共调度过的任务数量virtual uint64_t getTotal();
};//FoxThread 类是一个专门用于多线程任务调度的类,它通过事件驱动模型实现高效的任务处理。它通过 libevent 库的事件循环机制,
//能够在后台不断监听和处理任务。每个 FoxThread 实例都代表一个独立的工作线程,负责执行分发到该线程的任务。通过提供灵活的
//任务调度接口,FoxThread 允许外部线程将任务分发到当前线程,并确保任务的顺序执行和线程安全。
//实现上,FoxThread 利用了 UNIX socketpair 管道机制实现线程间的通信,通过 m_read 和 m_write 套接字监听任务的到来。
//当任务通过 dispatch() 或 batchDispatch() 等接口被派发时,FoxThread 会将这些任务加入到任务队列中,并通过 m_write 
//通知线程执行任务。在事件循环中,FoxThread 会持续等待来自 m_read 的可读事件,一旦有任务被写入,它就会触发回调函数并
//处理任务。通过这种方式,FoxThread 实现了任务的异步调度和高效处理,适用于高并发的多线程应用。
class FoxThread : public IFoxThread {
public:typedef std::shared_ptr<FoxThread> ptr;typedef IFoxThread::callback callback;//初始化回调函数的函数typedef std::function<void(FoxThread*)> init_cb;//name:该线程的名字//base:事件循环的上下文//事件循环:持续等待事件并处理事件的循环机制,本质是这样一种逻辑// while (running) {//     等待某个事件(网络、定时器、信号、通知等);//     有事件发生就分发给对应的处理回调函数;// }// 在 libevent 中,这个循环是由 event_base_dispatch() 或 event_base_loop() 来运行的,背后由 event_base* 这个结构管理。FoxThread(const std::string& name, struct event_base* base = NULL);~FoxThread();//获取的当前线程关联的FoxThread实例static FoxThread* GetThis();static const std::string& GetFoxThreadName();//程序可能创建多个 FoxThread 实例,GetAllFoxThreadName() 允许开发者一次性获取所有线程的名称//存放threadId----threadNamestatic void GetAllFoxThreadName(std::map<uint64_t, std::string>& name);//将当前 FoxThread 实例绑定到线程局部存储中(例如 TLS)。void setThis();//解绑当前线程中的 FoxThread 实例。void unsetThis();bool dispatch(callback cb) override;bool dispatch(uint32_t id, callback cb) override;bool batchDispatch(const std::vector<callback>& cbs) override;void broadcast(callback cb) override;void start() override;void stop() override;void join() override;void dump(std::ostream& os) override;uint64_t getTotal() override;//检查线程是否已经启动bool isStart() const;//获取事件基础结构struct event_base* getBase();//获取当前线程的idstd::thread::id getId() const;//获取某个名字下绑定的私有数据void* getData(const std::string& name);template<typename T>T* getData(const std::string& name);//在线程中存储私有数据void setData(const std::string& name, void* v);//初始化回调void setInitCb(init_cb v);private://FoxThread的主线程循环函数,由std::thread启动时执行//thread_cb() 是 线程主函数,它负责:// 初始化 event_base(即事件循环上下文);// 创建事件监听器(监听 m_read);// 将 read_cb 注册为 m_read 的回调函数;// 启动事件循环(通过 event_base_dispatch());void thread_cb();//与事件系统绑定的回调函数,当m_read可读时(说明主线程发来了任务)触发//从任务队列中取出任务,并顺序执行//触发条件://主线程通过 m_write 套接字写入任意内容时,会触发 m_read 的可读事件,从而执行 read_cb。static void read_cb(evutil_socket_t sock, short which, void* args);private://这两个成员是UNIX socketpair管道的两个端点,用于线程间通信evutil_socket_t m_read; //在FoxThread所在线程中监听此管道,一旦有数据读入,就唤醒event loop执行任务evutil_socket_t m_write;//在主线程或外部线程调用dispatch时使用,把任务写入通道//libevent的"事件循环"核心结构,代表一个事件多路复用的运行环境//负责// 管理所有注册的 event(比如监听 m_read);// 负责阻塞等待 IO 事件并触发回调;// 类似于 epoll_wait() 背后的调度器。struct event_base* m_base;//代表绑定到m_read管道上的读事件// 会设置成“可读时回调 read_cb()”;// 一旦有任务被 dispatch() 派发到当前线程,就通过 m_write 写入一个字节唤醒这个事件。struct event* m_event;//该FoxThread实际运行的工作线程//当这个线程被启动时,它运行的主函数是thread_cb()std::thread* m_thread;//保护共享资源myproject::RWMutex m_mutex;//该任务队列来存放所有通过dispatch()等接口派发到该线程的回调函数std::list<callback> m_callbacks;//线程的名称std::string m_name;//初始化回调函数//在thread_cb启动前调用//通常用于给当前线程初始化上下文、设置线程局部变量等。init_cb m_initCb;//标记当前线程是否在处理任务的状态标志bool m_working;//标志线程是否已经启动bool m_start;//记录当前线程累计处理的任务数量,便于监控、统计用途。uint64_t m_total;//为当前FoxThread绑定一些自定义的数据对象std::map<std::string, void*> m_datas;
};//FoxThreadPool 类是一个多线程池的实现,旨在通过预先创建多个线程来高效地处理任务。它通过对多个 FoxThread 实例的管理,
// 使得任务能够被快速调度到空闲的线程中,从而提升了任务处理的效率。在任务调度时,FoxThreadPool 会根据当前的线程池状态,
// 决定是将任务随机分配到某个线程,还是指定某个线程来处理任务。同时,它还支持批量任务调度,能够一次性将多个任务分配给线程
// 池中的线程进行处理。
//在实现上,FoxThreadPool 维护了一个 FoxThread 对象的列表,并通过 m_freeFoxThreads 列表来管理空闲的线程。
// 任务被添加到一个等待队列中,当有线程空闲时,它会取出任务并执行。FoxThreadPool 还支持线程池的初始化回调,
// 使得每个线程在启动时执行特定的初始化操作。通过这些机制,FoxThreadPool 可以高效地复用线程、调度任务,
// 并在任务负载增加时动态处理任务分发。
class FoxThreadPool : public IFoxThread {
public:typedef std::shared_ptr<FoxThreadPool> ptr;typedef IFoxThread::callback callback; //size:线程池中要创建多少个线程//name:线程池的名字//advance:是否使用高级模式/调度增强模式FoxThreadPool(uint32_t size, const std::string& name = "", bool advance = false);~FoxThreadPool();//随机线程执行bool dispatch(callback cb) override;bool batchDispatch(const std::vector<callback>& cb) override;//指定线程执行bool dispatch(uint32_t id, callback cb) override;//从线程列表中随机获取一个线程FoxThread* getRandFoxThread();//指定线程启动时要执行的初始化动作void setInitCb(FoxThread::init_cb v) { m_initCb = v;}void start() override;void stop() override;void join() override;void dump(std::ostream& os) override;void broadcast(callback cb) override;uint64_t getTotal() override { return m_total;}private://回收线程void releaseFoxThread(FoxThread* t);//其作用通常是检查有没有空闲线程、是否该唤醒线程来处理等待队列中的任务。void check();// 包装任务回调,增加额外逻辑void wrapcb(std::shared_ptr<FoxThread>, callback cb);private://线程池中的线程数量,构造时指定uint32_t m_size;//当前分发用的线程索引,用于轮询分发任务uint32_t m_cur;//线程池的名字std::string m_name;bool m_advance;bool m_start;RWMutex m_mutex;//等待执行的任务列表,当前没有空闲线程时可临时存放任务std::list<callback> m_callbacks;std::vector<FoxThread*> m_threads;//当前空闲可调度的线程集合,用于高效复用线程std::list<FoxThread*> m_freeFoxThreads;//线程初始化时要执行的回调函数FoxThread::init_cb m_initCb;//已调度任务的总数uint64_t m_total;
};//FoxThreadManager 类是一个管理多个线程池和线程实例的中央管理器。它提供了对不同线程池的任务调度、状态管理和资源控制功能。
// 通过这个类,开发者可以方便地向指定线程池派发任务,不仅支持单个任务的分配,还支持批量任务和广播任务,极大地简化了多线程任
// 务管理的复杂度。此外,FoxThreadManager 还可以将线程池的状态信息打印出来,便于监控和调试系统的运行状态。它的核心作用是
// 将线程池和线程实例按照名字进行管理,并提供简单的接口进行操作。
//实现上,FoxThreadManager 使用一个 std::map 数据结构来维护线程池或线程对象与其对应的名字之间的映射关系。通过这种映射,
// 管理器可以快速定位到指定的线程池或线程实例,并对其进行任务调度、状态打印等操作。在初始化和启动时,管理器会启动所有注册
// 的线程池或线程对象,停止时则会释放资源。FoxThreadManager 作为一个全局单例,确保了全系统范围内的线程管理和任务调度的
// 统一性和高效性。
class FoxThreadManager {
public:typedef IFoxThread::callback callback;//向名为name的线程池中随机选择一个线程派发任务。void dispatch(const std::string& name, callback cb);//向名为name的线程池中的 指定线程 派发任务。void dispatch(const std::string& name, uint32_t id, callback cb);// 向名为name的线程池中 批量派发多个任务。void batchDispatch(const std::string& name, const std::vector<callback>& cbs);// 向指定名字的线程池中的 所有线程广播执行同一个任务回调。void broadcast(const std::string& name, callback cb);// 将所有线程池或线程对象的状态信息打印到输出流。void dumpFoxThreadStatus(std::ostream& os);// 初始化线程管理器void init();// 启动所有注册进来的线程池或线程对象。void start();//停止所有注册的线程池或线程对象,释放资源。void stop() ;// 获取指定名称的线程池/线程实例。IFoxThread::ptr get(const std::string& name);// 向管理器中注册一个线程池或线程对象。void add(const std::string& name, IFoxThread::ptr thr);private://维护一个名字到线程/线程池之间的映射std::map<std::string, IFoxThread::ptr> m_threads;
};typedef myproject::Singleton<FoxThreadManager> FoxThreadMgr;}#endif

threadPool.cpp

#include "fox_thread.h"
#include "myproject/config.h"
#include "myproject/log.h"
#include "myproject/util.h"
#include "myproject/macro.h"
#include "myproject/config.h"
#include <iomanip>namespace myproject {static myproject::Logger::ptr g_logger = myproject_LOG_NAME("system");static myproject::ConfigVar<std::map<std::string, std::map<std::string, std::string> > >::ptr g_thread_info_set= Config::Lookup("fox_thread", std::map<std::string, std::map<std::string, std::string> >(),"confg for thread");//全局静态变量,用于跨多个 FoxThread 实例共享数据
static RWMutex s_thread_mutex;
static std::map<uint64_t, std::string> s_thread_names;
//线程局部变量,每个线程都有自己独立一份s_thread指针
//我们可以通过FoxThread::GetThis()拿到当前线程的FoxThread*实例
thread_local FoxThread* s_thread = nullptr;void FoxThread::read_cb(evutil_socket_t sock, short which, void* args) {FoxThread* thread = static_cast<FoxThread*>(args);uint8_t cmd[4096];if (recv(sock, cmd, sizeof(cmd), 0) > 0) {std::list<callback> callbacks;RWMutex::WriteLock lock(thread->m_mutex);callbacks.swap(thread->m_callbacks);lock.unlock();thread->m_working = true;//auto:std::list<std::function<void()>>::iteratorfor (std::list<std::function<void()>>::iterator it = callbacks.begin(); it != callbacks.end(); ++it) {if (*it) {try {(*it)();}  catch (std::exception& ex) {myproject_LOG_ERROR(g_logger) << "exception:" << ex.what();} catch (const char* c) {myproject_LOG_ERROR(g_logger) << "exception:" << c;} catch (...) {myproject_LOG_ERROR(g_logger) << "uncatch exception";}} else {//如果回调为空,说明要停止线程//中断事件循环,让线程退出event_base_loopbreak(thread->m_base);thread->m_start = false;//清空线程局部存储thread->unsetThis();break;}}myproject::Atomic::addFetch(thread->m_total, callbacks.size());}
}FoxThread::FoxThread(const std::string& name, struct event_base* base):m_read(0),m_write(0),m_base(NULL),m_event(NULL),m_thread(NULL),m_name(name),m_working(false),m_start(false),m_total(0) {int fds[2];if(evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {throw std::logic_error("thread init error");}//设置套接字为非阻塞// 通过设置 m_read 和 m_write 为非阻塞套接字,FoxThread 可以保持高效、// 响应迅速的事件循环。它不必在等待任务时被阻塞,而是可以继续执行其他任务// 或周期性检查是否有新的任务。这种设计使得 FoxThread 能够以事件驱动的方// 式处理大量任务,并确保不会因为 I/O 操作的延迟而影响到整体的线程性能。evutil_make_socket_nonblocking(fds[0]);evutil_make_socket_nonblocking(fds[1]);m_read = fds[0];m_write = fds[1];if (base) {//当传入外部event_base*,意味着当前线程通常是主线程,将直接使用FoxThread的事件循环//主线程负责初始化 FoxThreadManager 和线程池,启动和管理所有子线程的生命周期(创建、启动、停止、回收等)。//而start中的子线程用来通过 libevent 事件循环等待并处理分发过来的任务(callback),执行异步任务逻辑。m_base = base;//将当前FoxThread绑定到线程setThis();} else {m_base = event_base_new();}//创建事件对象:// 使用 event_new 函数为 m_read(接收端套接字)创建一个新的事件。// EV_READ 表示当套接字可读时触发该事件。// EV_PERSIST 表示事件持续存在,即在事件触发后仍然有效。// read_cb 是触发事件时调用的回调函数,this 作为参数传递给回调函数。m_event = event_new(m_base, m_read, EV_READ | EV_PERSIST, read_cb, this);//添加事件到事件循环event_add(m_event, NULL);
}void FoxThread::dump(std::ostream& os) {RWMutex::ReadLock lock(m_mutex);os << "[thread name=" << m_name<< " working=" << m_working<< " tasks=" << m_callbacks.size()<< " total=" << m_total<< "]" << std::endl;
}std::thread::id FoxThread::getId() const {if (m_thread) {return m_thread->get_id();}//返回一个默认构造的id对象return std::thread::id();
}void* FoxThread::getData(const std::string& name) {auto it = m_datas.find(name);return it == m_datas.end() ? nullptr : it->second;
}void  FoxThread::setData(const std::string& name, void* v) {//Mutex::WriteLock lock(m_mutex);m_datas[name] = v;
}FoxThread::~FoxThread() {if (m_read) {close(m_read);}if (m_write) {close(m_write);}stop();join();if (m_thread) {delete m_thread;}if (m_event) {event_free(m_event);}if (m_base) {event_base_free(m_base);}
}void FoxThread::start() {if (m_thread) {throw std::logic_error("FoxThread is running");}//start后会创建一个新的子线程//这个子线程的目的就是让它在后台一直处理异步事件//让这个线程进入一个“事件驱动循环”中,它阻塞的不是 I/O,//而是等待事件(如 socket 可读、超时、信号等)触发回调。m_thread = new std::thread(std::bind(&FoxThread::thread_cb, this));m_start = true;
}void FoxThread::thread_cb() {setThis();//为当前线程设置名称pthread_setname_np(pthread_self(), m_name.substr(0,15).c_str());if (m_initCb) {//如果设置了初始化回调m_initCb(this);m_initCb = nullptr;}//启动libevent的事件循环,监听和处理事件。该线程将进入阻塞状态,持续等待任//务事件的触发并进行处理,直到事件循环被外部中止(如停止线程时)。event_base_loop(m_base, 0);
}// 将一个回调任务添加到线程的任务队列中,并通过 socket 激活事件循环,使子线程去处理这个任务。
bool FoxThread::dispatch(callback cb) {RWMutex::WriteLock lock(m_mutex);m_callbacks.push_back(cb);lock.unlock();uint8_t cmd = 1;if (send(m_write, &cmd, sizeof(cmd), 0) <= 0) {return false;}return true;
}bool FoxThread::dispatch(uint32_t id, callback cb) {return dispatch(cb);
}bool FoxThread::batchDispatch(const std::vector<callback>& cbs) {RWMutex::WriteLock lock(m_mutex);for (auto i : cbs) {m_callbacks.push_back(i);}lock.unlock();uint8_t cmd = 1;if (send(m_write, &cmd, sizeof(cmd), 0) <= 0) {return false;}return true;
}void FoxThread::broadcast(callback cb) {dispatch(cb);
}void FoxThread::stop() {RWMutex::WriteLock lock(m_mutex);m_callbacks.push_back(nullptr);if (m_thread) {uint8_t cmd = 0;send(m_write, &cmd, sizeof(cmd), 0);}
}void FoxThread::join() {if (m_thread) {m_thread->join();//delete 会释放堆内存,但不会把指针本身清空,指针仍然保存着原先的地址。如果之后不小心访问这个指针//(例如再次使用 *m_thread),就会访问已被释放的内存,行为是未定义的(undefined behavior),非常危险。//设置为 NULL/nullptr 后,访问就变成空指针访问,可以被程序快速识别和拦截//(很多平台下会直接崩溃在访问空指针的位置),更容易调试。delete m_thread;m_thread = NULL;}
}FoxThread* FoxThread::GetThis() {return s_thread;
}void FoxThread::setThis() {m_name = m_name + "_" + std::to_string(myproject::GetThreadId());s_thread = this;RWMutex::WriteLock lock(s_thread_mutex);s_thread_names[myproject::GetThreadId()] = m_name;
}void FoxThread::unsetThis() {s_thread = nullptr;RWMutex::WriteLock lock(s_thread_mutex);s_thread_names.erase(myproject::GetThreadId());
}const std::string& FoxThread::GetFoxThreadName() {FoxThread* t = GetThis();if (t) {return t->m_name;}uint64_t tid = myproject::GetThreadId();do {RWMutex::ReadLock lock(s_thread_mutex);auto it = s_thread_names.find(tid);if(it != s_thread_names.end()) {return it->second;}} while(0);do {RWMutex::WriteLock lock(s_thread_mutex);s_thread_names[tid] = "UNNAME_" + std::to_string(tid);return s_thread_names[tid];} while (0);
}void FoxThread::GetAllFoxThreadName(std::map<uint64_t, std::string>& names) {RWMutex::WriteLock lock(s_thread_mutex);names.insert(s_thread_names.begin(), s_thread_names.end());
}FoxThreadPool::FoxThreadPool(uint32_t size, const std::string& name, bool advance):m_size(size),m_cur(0),m_name(name),m_advance(advance),m_start(false),m_total(0) {m_threads.resize(size);for (size_t i = 0; i < size; ++i) {FoxThread* t(new FoxThread(name + "_" + std::to_string(i)));m_threads[i] = t;}
}FoxThreadPool::~FoxThreadPool() {for(size_t i = 0; i < m_size; ++i) {delete m_threads[i];}
}void FoxThreadPool::start() {for(size_t i = 0; i < m_size; ++i) {m_threads[i]->setInitCb(m_initCb);m_threads[i]->start();m_freeFoxThreads.push_back(m_threads[i]);}if(m_initCb) {m_initCb = nullptr;}m_start = true;check();
}void FoxThreadPool::stop() {for(size_t i = 0; i < m_size; ++i) {m_threads[i]->stop();}m_start = false;
}void FoxThreadPool::join() {for(size_t i = 0; i < m_size; ++i) {m_threads[i]->join();}
}void FoxThreadPool::releaseFoxThread(FoxThread* t) {{RWMutex::WriteLock lock(m_mutex);m_freeFoxThreads.push_back(t);}check();
}bool FoxThreadPool::dispatch(callback cb) {{myproject::Atomic::addFetch(m_total, (uint64_t)1);RWMutex::WriteLock lock(m_mutex);if (!m_advance) {//如果不是高级调度模式,则直接将任务cb分发给某个线程,这是一种轮询调度策略,平均分到每个线程return m_threads[m_cur++ % m_size]->dispatch(cb);}//如果是高级调度模式,则先将任务放入等待队列中,等待空闲线程来拉取任务m_callbacks.push_back(cb);}check();return true;
}bool FoxThreadPool::batchDispatch(const std::vector<callback>& cbs) {myproject::Atomic::addFetch(m_total, cbs.size());RWMutex::WriteLock lock(m_mutex);if(!m_advance) {for(auto cb : cbs) {m_threads[m_cur++ % m_size]->dispatch(cb);}return true;}for(auto cb : cbs) {m_callbacks.push_back(cb);}lock.unlock();check();return true;
}void FoxThreadPool::wrapcb(std::shared_ptr<FoxThread> thr, callback cb) {cb();
}bool FoxThreadPool::dispatch(uint32_t id, callback cb) {myproject::Atomic::addFetch(m_total, (uint64_t)1);return m_threads[id % m_size]->dispatch(cb);
}FoxThread* FoxThreadPool::getRandFoxThread() {return m_threads[m_cur++ % m_size];
}void FoxThreadPool::broadcast(callback cb) {for(size_t i = 0; i < m_threads.size(); ++i) {m_threads[i]->dispatch(cb);}
}void FoxThreadPool::dump(std::ostream& os) {RWMutex::ReadLock lock(m_mutex);os << "[FoxThreadPool name = " << m_name << " thread_count = " << m_threads.size()<< " tasks = " << m_callbacks.size() << " total = " << m_total<< " advance = " << m_advance<< "]" << std::endl;for(size_t i = 0; i < m_threads.size(); ++i) {os << "    ";m_threads[i]->dump(os);}
}           // 检查是否有空闲线程和待处理的回调任务,
// 如果有,则将任务派发到线程中执行
void FoxThreadPool::check() {do {// 如果线程池尚未启动,直接退出if (!m_start) {break;}// 加写锁,确保对线程池内部数据结构的修改是线程安全的RWMutex::WriteLock lock(m_mutex);// 如果没有空闲线程或没有待处理的任务,直接退出if (m_freeFoxThreads.empty() || m_callbacks.empty()) {break;}// 从空闲线程列表中取出一个线程(注意用 shared_ptr 包装,带自定义 deleter)std::shared_ptr<FoxThread> thr(m_freeFoxThreads.front(), std::bind(&FoxThreadPool::releaseFoxThread, this, std::placeholders::_1));m_freeFoxThreads.pop_front();// 取出一个待执行的回调任务callback cb = m_callbacks.front();m_callbacks.pop_front();// 解锁,因为后续执行任务派发可能较慢,不希望锁持有太久lock.unlock();// 如果线程已经启动,则派发任务(wrapcb 用于包装用户回调)if (thr->isStart()) {thr->dispatch(std::bind(&FoxThreadPool::wrapcb, this, thr, cb));} else {// 如果线程尚未启动,则将任务重新放回任务队列RWMutex::WriteLock lock(m_mutex);m_callbacks.push_front(cb);}} while (true); // 保证整个过程在可处理条件下至少执行一次,否则立即退出
}IFoxThread::ptr FoxThreadManager::get(const std::string& name) {auto it = m_threads.find(name);return it == m_threads.end() ? nullptr : it->second;
}void FoxThreadManager::add(const std::string& name, IFoxThread::ptr thr) {m_threads[name] = thr;
}void FoxThreadManager::dispatch(const std::string& name, callback cb) {IFoxThread::ptr ti = get(name);myproject_ASSERT(ti);ti->dispatch(cb);
}void FoxThreadManager::dispatch(const std::string& name, uint32_t id, callback cb) {IFoxThread::ptr ti = get(name);myproject_ASSERT(ti);ti->dispatch(id, cb);
}void FoxThreadManager::batchDispatch(const std::string& name, const std::vector<callback>& cbs) {IFoxThread::ptr ti = get(name);myproject_ASSERT(ti);ti->batchDispatch(cbs);
}void FoxThreadManager::broadcast(const std::string& name, callback cb) {IFoxThread::ptr ti = get(name);myproject_ASSERT(ti);ti->broadcast(cb);
}void FoxThreadManager::dumpFoxThreadStatus(std::ostream& os) {os << "FoxThreadManager: " << std::endl;for(auto it = m_threads.begin();it != m_threads.end(); ++it) {it->second->dump(os);}os << "All FoxThreads:" << std::endl;std::map<uint64_t, std::string> names;FoxThread::GetAllFoxThreadName(names);for(auto it = names.begin();it != names.end(); ++it) {os << std::setw(30) << it->first<< ": " << it->second << std::endl;}
}void FoxThreadManager::init() {auto m = g_thread_info_set->getValue();for (auto i : m) {//当前线程池需要的线程数auto num = myproject::GetParamValue(i.second, "num", 0);//线程或线程池的名auto name = i.first;auto advance = myproject::GetParamValue(i.second, "advance", 0);if(num <= 0) {myproject_LOG_ERROR(g_logger) << "thread pool:" << name<< " num:" << num<< " advance:" << advance<< " invalid";continue;}if (num == 1) {m_threads[name] = FoxThread::ptr(new FoxThread(name));} else {m_threads[name] = FoxThreadPool::ptr(new FoxThreadPool(num, name, advance));}}
}void FoxThreadManager::start() {for(auto i : m_threads) {myproject_LOG_INFO(g_logger) << "thread: " << i.first << " start begin";i.second->start();myproject_LOG_INFO(g_logger) << "thread: " << i.first << " start end";}
}void FoxThreadManager::stop() {for(auto i : m_threads) {myproject_LOG_INFO(g_logger) << "thread: " << i.first << " stop begin";i.second->stop();myproject_LOG_INFO(g_logger) << "thread: " << i.first << " stop end";}for(auto i : m_threads) {myproject_LOG_INFO(g_logger) << "thread: " << i.first << " join begin";i.second->join();myproject_LOG_INFO(g_logger) << "thread: " << i.first << " join end";}
} }

FoxThread的实现原理

FoxThread 是基于 libevent 实现的事件驱动线程,核心通过 socketpair 建立主线程与子线程的通信管道,子线程运行事件循环等待 m_read 上的读事件。主线程调用 dispatch() 提交任务并写入 m_write 唤醒线程,触发 read_cb() 执行任务队列中的回调。任务通过 mutex 加锁保护,并支持中断事件循环退出线程。整体设计轻量、异步高效,适用于高并发任务调度场景。

             +-------------------------+|     FoxThread 构造     ||-------------------------|| 创建 socketpair         || 设置非阻塞              || 初始化 event_base       || 创建 read_cb 事件       |+-----------+-------------+|v+-------------------+| 调用 start()      | ← 外部线程调用(如主线程)+-------------------+|v+--------------------------+| 创建 std::thread 子线程 || 执行 thread_cb()        |+-----------+--------------+|v+-------------------------------------+| thread_cb():                        || 设置线程名 + 执行初始化回调(可选) || 启动 event_base_loop() 事件循环     |+----------------+--------------------+|| (阻塞等待事件)|+----------------v---------------------------+| 主线程调用 dispatch(cb) 向 m_callbacks 添加 || 并通过 m_write 写入 socket 激活事件         |+----------------+---------------------------+|v+-----------------------+| read_cb 被触发        | ← 事件触发点(套接字可读)|-----------------------|| 读取 socket           || 获取所有 callback     || 执行 callback()       || 统计执行次数          || 若 callback=null 停止 |+-----------+-----------+|v+--------------------------+| 线程退出准备              || event_base_loopbreak()   || unsetThis() 清理上下文   |+--------------------------+

FoxThread 的整个生命周期中,从构造到销毁,它主要围绕事件驱动和任务派发这两大核心进行设计。线程在构造时首先通过 socketpair 创建一对本地通信套接字 m_readm_write,并设置为非阻塞模式。这对套接字的作用是:主线程向 m_write 写入数据,以唤醒子线程监听的 m_read,触发事件循环中注册的 read_cb 回调函数。

随后,FoxThread 通过 event_base_new() 创建一个事件基础对象,并将 read_cb 回调函数注册到 m_read 套接字的 EV_READ | EV_PERSIST 事件上。此时,如果尚未传入外部的 event_base,则线程将自行管理其事件循环。

调用 start() 方法后,会创建一个新的子线程,在线程内部调用 thread_cb() 函数。在该函数中,线程首先通过 setThis() 将自己注册为线程局部实例,并执行可选的初始化回调,然后正式进入 event_base_loop() 的事件循环状态,开始等待任务事件。

任务的提交通过 dispatch() 完成,主线程将任务(即 callback)加入线程的 m_callbacks 队列中,并通过向 m_write 写入一个字节数据,唤醒 m_read 的可读事件。子线程接收到唤醒信号后,执行 read_cb() 回调函数,从 m_callbacks 中取出所有任务,逐个执行,每个任务都用 try-catch 块进行异常保护,确保线程的健壮性。

如果某一次任务为 nullptr,表示线程需要退出,这时 read_cb() 会调用 event_base_loopbreak() 跳出事件循环,并调用 unsetThis() 清理线程本地注册信息。

最后,外部线程可通过 join() 等待子线程安全退出,并自动释放资源。

这一机制实现了一个轻量级、高并发、事件驱动的线程任务执行模型,线程通过 libevent 事件循环持续运行,任务派发通过异步 socket 通知,既避免了繁重的同步阻塞,又兼顾了任务隔离与线程可控性,非常适合网络服务器、定时任务调度器等异步高效场景。

FoxThreadPool 的实现原理

FoxThreadPool 是基于多个 FoxThread 线程的池,旨在高效地分配和管理线程任务。其核心设计是通过将任务分发到多个线程上并利用任务队列进行调度,从而提高任务的并发执行能力,适合高并发任务调度和分布式处理场景。

             +-------------------------+|     FoxThreadPool 构造 ||-------------------------|| 创建多个 FoxThread      || 存储线程池大小与状态     |+-----------+-------------+|v+-------------------+| 调用 start()      | ← 外部线程调用(如主线程)+-------------------+|v+------------------------+| 启动所有子线程         || 调用 start() 执行      |+------------------------+|v+----------------------------------------+| 调度任务:主线程调用 dispatch()      || 或 batchDispatch() 进行任务提交     || 将任务插入 m_callbacks 队列         |+-----------------+----------------------+|v+--------------------------+| 任务分发到空闲线程池中的线程 || (根据调度策略分发)          |+--------------------------+|v+-------------------------+| 子线程接收并执行回调函数 || 任务处理完毕后回调 wrapcb |+-------------------------+|v+----------------------------+| 任务执行完毕后,线程返回池 || 线程池进行检查与任务重分配 |+----------------------------+

FoxThreadPool 的设计使得多线程池能够高效地管理和调度任务,通过分发任务到空闲线程,并使用事件驱动模型,最大化线程资源的利用率。在高并发场景下,能够保证线程池中的每个线程都有任务可执行,同时提供了灵活的任务分发机制和线程池管理策略,适合需要大量并发任务调度的应用场景。

FoxThreadManager 的实现原理

FoxThreadManager 是一个线程管理器,负责管理和调度多个线程(或线程池)。它通过维护一个线程名到线程实例的映射,允许外部代码通过线程名对线程进行操作。它支持线程的添加、获取、任务调度、广播任务等功能,适用于需要动态管理多个线程或线程池的高并发应用。

             +-------------------------+|     FoxThreadManager 构造 ||-------------------------|| 创建线程池 m_threads    |+-----------+-------------+|v+-------------------+| 调用 init()       | ← 外部线程调用(初始化线程池配置)+-------------------+|v+-------------------------+| 初始化线程池            || 设置线程数、名称等      || 将线程添加到 m_threads |+-------------------------+|v+-----------------------+| 调用 start()          | ← 外部线程调用(启动所有线程)+-----------------------+|v+--------------------------+| 每个线程调用 start()     || 启动事件循环与任务执行   |+--------------------------+|v+------------------------------------------+| 调度任务:主线程调用 dispatch()        || 向特定线程添加任务并执行回调            |+----------------+-------------------------+|v+----------------------------+| 执行任务:线程执行回调函数 |+----------------------------+|v+---------------------------+| 任务执行完毕,线程返回池 |+---------------------------+|v+---------------------------------+| 调用 stop() 停止所有线程池    || 调用 join() 等待线程退出      |+---------------------------------+

FoxThreadManager 通过维护一个线程池的映射,提供了灵活的线程管理能力。它支持根据线程名称获取线程、向线程分发任务、批量分发任务、广播任务以及线程池状态的管理和输出。通过初始化、启动、停止、等待线程等操作,FoxThreadManager 提供了一个高效、易用的线程管理工具,非常适合多线程并发处理、任务调度和分配。

相关文章:

基于libevent的异步事件驱动型线程池实现

----------------------| IFoxThread | ← 抽象线程接口|----------------------|| dispatch() || start() || stop() || ... |----------^-----------|--------------------|----------------------| …...

ArcGIS+InVEST+RUSLE:水土流失模拟与流域管理的高效解决方案;水土保持专题地图制作

在全球生态与环境面临严峻挑战的当下&#xff0c;水土流失问题已然成为制约可持续发展的重要因素之一。水土流失不仅影响土地资源的可持续利用&#xff0c;还对生态环境、农业生产以及区域经济发展带来深远影响。因此&#xff0c;科学、精准地模拟与评估水土流失状况&#xff0…...

#S4U2SELF#S4U2Proxy#CVE-2021-42278/42287

#S4U2SELF Win08创建普通用户 s4u2 xwj456 可以看到普通用户是没用委托属性的 Win08手动赋予委托服务属性 setspn -A wsw/wsw.com s4u2 Win10身份验证 s4u2 xwj456 AS请求 两个勾 两个勾和include-pac记得按上&#xff08;蓝色&#xff09; &#xff0c;发包之前把wiresh…...

利用基于LLM的概念提取和FakeCTI数据集提升网络威胁情报对抗虚假信息活动的能力

摘要 虚假新闻和虚假信息宣传活动的迅速蔓延对公众信任、政治稳定和网络安全构成了重大威胁。传统的网络威胁情报&#xff08;CTI&#xff09;方法依赖于域名和社交媒体账号等低级指标&#xff0c;很容易被频繁修改其在线基础设施的对手规避。为了解决这些局限性&#xff0c;我…...

uniapp|实现手机通讯录、首字母快捷导航功能、多端兼容(H5、微信小程序、APP)

基于uniapp实现带首字母快捷导航的通讯录功能,通过拼音转换库实现汉字姓名首字母提取与分类,结合uniapp的scroll-view组件与pageScrollTo API完成滚动定位交互,并引入uni-indexed-list插件优化索引栏性能。 目录 核心功能实现动态索引栏生成​联系人列表渲染​滚动定位联动性…...

使用PhpStudy搭建Web测试服务器

一、安装PhpStudy 从以下目录下载PhpStudy安装文件 Windows版phpstudy下载 - 小皮面板(phpstudy) (xp.cn) 安装成功之后打开如下界面 点击启动Apache 查看网站地址 在浏览器中输入localhost:88,出现如下页面就ok了 二、与Unity交互 1.配置下载文件路径&#xff0c;点击…...

Qt/C++面试【速通笔记九】—视图框架机制

在Qt中&#xff0c;QGraphicsView和QGraphicsScene是用于构建二维图形界面的核心组件。它们的设计使得开发者能够高效地管理和渲染图形项&#xff0c;支持丰富的用户交互&#xff0c;例如缩放、旋转、平移等。 1. QGraphicsScene和QGraphicsView的基本概念 QGraphicsScene QG…...

react-diff-viewer 如何实现语法高亮

前言 react-diff-viewer 是一个很好的 diff 展示库&#xff0c;但是也有一些坑点和不完善的地方&#xff0c;本文旨在描述如何在这个库中实现自定义语法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…...

Python实例题:Django搭建简易博客

目录 Python实例题 题目 1. 创建 Django 项目和应用 2. 配置项目 3. 设计模型 blog_app templates blog_app post_list.html admin.py models.py urls.py views.py blog_project urls.py 代码解释 models.py&#xff1a; admin.py&#xff1a; urls.py&…...

Kotlin 异步初始化值

在一个类初始化的时候或者方法执行的时候&#xff0c;总有一些值是需要的但是不是立即需要的&#xff0c;并且在需要的时候需要阻塞流程来等待值的计算&#xff0c;这时候异步的形式创建这个值是毋庸置疑最好的选择。 为了更好的创建值需要使用 Kotlin 的协程来创建&#xff0…...

扩展:React 项目执行 yarn eject 后的 config 目录结构详解

扩展&#xff1a;React 项目执行 yarn eject 后的 config 目录结构详解 什么是 yarn eject&#xff1f;React 项目执行 yarn eject 后的 config 目录结构详解&#x1f4c1; config 目录结构各文件作用详解env.jsgetHttpsConfig.jsmodules.jspaths.jswebpack.config.jswebpackDe…...

(自用)Java学习-5.8(总结,springboot)

一、MySQL 数据库 表关系 一对一、一对多、多对多关系设计外键约束与级联操作 DML 操作 INSERT INTO table VALUES(...) DELETE FROM table WHERE... UPDATE table SET colval WHERE...DQL 查询 基础查询&#xff1a;SELECT * FROM table WHERE...聚合函数&#xff1a;COUNT()…...

cursor 如何在项目内自动创建规则

在对话框内 / Generate。cursor rules 就会自动根据项目进行创建规则 文档来自&#xff1a;https://www.kdocs.cn/l/cp5GpLHAWc0p...

C++ 迭代器

1.用途&#xff1a; 像我们之前学习的容器map,vector等&#xff0c;如果需要遍历该怎么做呢&#xff1f;这些容器大部分对下标式遍历&#xff0c;无法像数组灵活使用&#xff0c;也包括增删改查&#xff0c;因为它们的特性&#xff0c;所以需要一种其他的方法。 那么迭代器就…...

基于微信小程序的城市特色旅游推荐应用的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...

最大m子段和

问题描述解题思路伪代码代码实现复杂度分析 问题描述 给定一个有n(n>0)个整数的序列&#xff0c;要求其m个互不相交的子段&#xff0c;使得这m个子段和最大。 输入&#xff1a;整数序列{nums}&#xff0c;m。 输出&#xff1a;最大m子段和。 对于m1的情况&#xff0c;即求最…...

4.MySQL全量、增量备份与恢复

1.数据备份的重要性 在企业中数据的价值至关重要&#xff0c;数据保障了企业业务的正常运行。因此&#xff0c;数据的安全性及数据的可靠性是运维的重中之重&#xff0c;任何数据的丢失都可能对企业产生严重的后果。通常情况下造成数据丢失的原因有如下几种&#xff1a; a.程…...

每日算法刷题Day4 5.12:leetcode数组4道题,用时1h

7. 704.二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; 思想 二分模版题 代码 c: class Solution { public:int search(vector<int>& nums, int target) {int nnums.size();int left0,rightn-1;int res-1;while(left<right){int midleft((…...

Day 15

目录 1.chika和蜜柑1.1 解析1.2 代码 2.对称之美2.1 解析2.2 代码 3.添加字符3.1 解析3.2 代码 1.chika和蜜柑 chika和蜜柑 TopK、堆、排序 1.1 解析 1.2 代码 #include <iostream> #include <vector> #include <algorithm> using namespace std; struct …...

脑机接口重点产品发展路径分析:以四川省脑机接口及人机交互产业攻坚突破行动计划(2025-2030年)为例

引言 随着人工智能和生物技术的飞速发展,脑机接口技术作为连接人类大脑与智能设备的桥梁,正在成为全球科技竞争的新焦点。2025年5月12日,四川省经济和信息化厅等8部门联合印发了《四川省脑机接口及人机交互产业攻坚突破行动计划(2025-2030年)》,为四川省在这一前沿领域的…...

leetcode 18. 四数之和

题目描述 和leetcode 15. 三数之和用同样的方法。有两个注意点。 一是剪枝的逻辑 这是和15. 三数之和 - 力扣&#xff08;LeetCode&#xff09;问题不同的地方。 无法通过这种情况&#xff1a; 二是整数溢出 最终答案 class Solution { public:vector<vector<int>…...

CentOS部署Collabora Online

1.安装Docker CentOS7安装Docker(超详细)-CSDN博客 2.拉取镜像 docker pull collabora/code:latest 3. 启动容器&#xff08;直接暴露HTTP端口&#xff09; docker run -d --name collabora -p 9980:9980 -e "usernameadmin" -e "password123456" -e …...

《Spring Boot 4.0新特性深度解析》

Spring Boot 4.0的发布标志着Java生态向云原生与开发效能革命的全面迈进。作为企业级应用开发的事实标准框架&#xff0c;此次升级在运行时性能、云原生支持、开发者体验及生态兼容性四大维度实现突破性创新。本文深度解析其核心技术特性&#xff0c;涵盖GraalVM原生镜像支持、…...

FFmpeg 与 C++ 构建音视频处理全链路实战(一)—— 环境配置与视频解封装

在数字媒体的浩瀚宇宙中&#xff0c;FFmpeg 就像一艘功能强大的星际战舰&#xff0c;承载着处理音视频数据的重任。而 C 作为一门高效、灵活的编程语言&#xff0c;犹如一位技艺精湛的星际工程师&#xff0c;能够精准操控 FFmpeg 战舰&#xff0c;完成各类复杂的音视频处理任务…...

什么是 NoSQL 数据库?它与关系型数据库 (RDBMS) 的主要区别是什么?

我们来详细分析一下 NoSQL 数据库与关系型数据库 (RDBMS) 的主要区别。 什么是 NoSQL 数据库&#xff1f; NoSQL (通常指 “Not Only SQL” 而不仅仅是 “No SQL”) 是一类数据库管理系统的总称。它们的设计目标是解决传统关系型数据库 (RDBMS) 在某些场景下的局限性&#xf…...

AI需求分析话术 | DeepSeek R1

运行环境&#xff1a;jupyter notebook (python 3.12.7) Dash 场景&#xff1a; 收集了小程序的问题点和优化建议&#xff0c;一键AI分析&#xff0c;快速排优先级 指令话术&#xff1a; 对收集的小程序问题点和建议&#xff0c;做需求分析并总结形成报告&#xff0c;报告结构…...

【Redis】键值对数据库实现

目录 1、背景2、五种基本数据类型对应底层实现3、redis数据结构 1、背景 redis是一个&#xff08;key-value&#xff09;键值对数据库&#xff0c;其中value可以是五大基本数据类型&#xff1a;string、list、hash、set、zset&#xff0c;这五大基本数据类型对应着不同的底层结…...

MySQL 8.0 OCP 英文题库解析(三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题16~25 试题16:…...

互联网大厂Java求职面试:优惠券服务架构设计与AI增强实践-1

互联网大厂Java求职面试&#xff1a;优惠券服务架构设计与AI增强实践-1 在一间简洁明亮的会议室里&#xff0c;郑薪苦正面对着一位技术总监级别的面试官&#xff0c;这位面试官拥有超过十年的大型互联网企业经验&#xff0c;以技术全面性与落地能力著称。 第一轮面试&#xf…...

object的常用方法

在面向对象编程中&#xff0c;Object 类是所有类的根类&#xff0c;它提供了一些基本的方法&#xff0c;这些方法可以被所有对象继承和使用。以下是一些在 Java 中 Object 类的常用方法&#xff0c;以及它们的作用和使用示例&#xff1a; 1. equals(Object obj) 作用&#xff…...

解决vue create 创建项目,不能使用上下键选择模板的问题

使用 git bash 创建vue项目时候&#xff0c;无法使用上下键盘按键选择创建模板 处理&#xff1a; 1.当前界面&#xff0c;按CTR C终止创建命令&#xff1b; 2.使用 alias vuewinpty vue.cmd&#xff0c;更新命令环境&#xff1b; 3.再次使用 vue create demo创建项目&#xf…...

AI Agent开发第64课-DIFY和企业现有系统结合实现高可配置的智能零售AI Agent(上)

开篇 我们之前花了将近10个篇章讲Dify的一些基础应用,包括在讲Dify之前我们讲到了几十个AI Agent的开发例子,我不知道大家发觉了没有,在AI Agent开发过程中我们经常会伴随着这样的一些问题: 需要经常改猫娘;需要经常改调用LLM的参数,甚至在一个流程中有3个节点,每个节点…...

3.Redis-set集合类型

1.用集合做差集、并集&#xff08;共同关注&#xff09;、交集...

软考 系统架构设计师系列知识点之杂项集萃(57)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之杂项集萃&#xff08;56&#xff09; 第93题 美国著名的卡内基梅隆大学软件工程学研究所针对软件工程的工程管理能力与水平进行了充分研究&#xff0c;提出了5级管理能力的模式&#xff0c;包括临时凑合阶段、简单模仿…...

Cabot:开源免费的 PagerDuty 替代品,让系统监控更简单高效

在当今复杂的IT环境中,及时发现并解决系统问题至关重要。而Cabot作为一款开源免费的监控工具,为开发和运维团队提供了强大而简单的解决方案。本文将详细介绍Cabot的核心功能、优势以及快速部署方法,帮助你更好地保障系统稳定性。 Cabot简介 Cabot是一个功能类似PagerDuty的开…...

AI中的MCP是什么?MCP的作用及未来方向预测 (使用go-zero 快速搭建MCP服务器)

AI是当下最热的风。在当今AI技术飞速发展的时代&#xff0c;AI的应用已经渗透到我们日常生活的方方面面。然而&#xff0c;随着AI系统的复杂性不断增加&#xff0c;如何让AI具备更强的自主性和灵活性成为了业界关注的焦点。这就引出了Model Context Protocol&#xff08;MCP&am…...

字节开源FlowGram与n8n 技术选型

字节跳动开源的 FlowGram 和 n8n 是两款功能强大但定位不同的工作流编排工具&#xff0c;以下是两者的技术选型对比分析&#xff0c;结合其核心特性、适用场景和优劣势&#xff1a; 一、核心特性对比 维度FlowGram&#xff08;字节开源&#xff09;n8n定位面向AI场景的可视化工…...

面试专栏-03-Git的常用命令

二、Git常用命令学习 git本质上&#xff0c;就是一个 git类型的文件夹 1、基础配置信息 git -v&#xff1a;查看 git 版本信息 git config --global user.name "dz.cn"&#xff1a;配置用户名&#xff0c;注意&#xff0c;这里配置的用户名在进行版本提交时&#xf…...

使用 Syncthing 在两台电脑之间同步文件:简单教程

&#x1f9e9; 什么是 Syncthing&#xff1f; Syncthing 是一个开源、跨平台、点对点的文件同步工具&#xff0c;类似于 Dropbox&#xff0c;但不依赖第三方服务器。它直接在你的设备之间同步文件&#xff0c;更加安全、可控&#xff0c;非常适合个人或团队内部使用。 支持操…...

spdlog日志格式化 标志全指南

一、spdlog格式化核心机制 SPDLOG通过set_pattern()函数实现灵活的日志格式定制&#xff0c;该函数解析用户提供的格式字符串&#xff0c;生成包含时间、源代码、进程等信息的结构化日志。其底层由pattern_formatter类处理&#xff0c;通过识别%标志符的组合动态生成格式化器对…...

http接口性能优化方案

设计高响应时间的HTTP查询接口&#xff08;<80ms&#xff09; 要实现跨机房调用的HTTP接口并保持响应时间在80ms以下&#xff0c;确实面临多个技术挑战。以下是关键点和解决方案&#xff1a; 主要技术难点 网络延迟&#xff1a;跨机房物理距离导致的传输延迟 TCP握手/挥手…...

Express知识框架

一、核心概念 1. Express 简介 Node.js 的 Web 框架&#xff0c;提供 HTTP 服务器封装 轻量级但灵活&#xff0c;支持中间件扩展 基于路由&#xff0c;支持 RESTful API 和传统 MVC 架构 无内置 ORM 或模板引擎&#xff0c;但可集成第三方库 2. 核心对象 express() - 创建…...

调出事件查看器界面的4种方法

方法1. 方法2. 方法3. 方法4....

Bash 执行命令的基本流程

是的&#xff0c;Bash 在执行外部命令&#xff08;如 ls、grep 等非内置命令&#xff09;时&#xff0c;确实会调用 exec 系列函数来实现进程程序替换。以下是其底层机制的分步解析&#xff1a; 1. Bash 执行命令的基本流程 当在 Bash 中键入一个命令&#xff08;例如 ls -l&a…...

我们来学mysql -- 安装8.4版本

8.4版本 下载解压用户目录&用户权限my.cnf初始化普通启动safe启动检查启动用户登录远程登录用户root% 下载 地址选择安装包 查看OS位数 getconf LONG_BIT 二进制安装包说明 二进制包的文件名会包含 linux 或 glibc 等字样如&#xff1a;mysql-8.4.4-linux-glibc2.28-x86_…...

Java MVC架构在当今时代的技术解析

一、前言 MVC&#xff08;Model-View-Controller&#xff09;架构作为经典的设计模式&#xff0c;经历了数十年的演进。尽管新兴技术层出不穷&#xff0c;Java MVC仍然在企业级开发中占据重要地位。 二、Java MVC核心优势 1. 模块化分层设计 职责分离&#xff1a;数据层&…...

FPGA----基于ZYNQ 7020实现定制化的EPICS程序开发

引言:基于前文,我们在FPGA侧实现了一些外设驱动功能,并将其导出为hdf生成了他的petalinux,借助ALINX的Debian8做了我们自己的根文件系统。现在,我们需要在petalinux下开发一个epics程序,可以调用我们FPGA的驱动。 1、整体程序架构 注意:我们基于ALINX的根文件系统是不完…...

配置hosts

打开文件 右键点击「记事本」或其他文本编辑器&#xff0c;选择「以管理员身份运行」。 打开路径&#xff1a;C:\Windows\System32\drivers\etc\hosts 添加映射 在文件末尾添加一行&#xff0c;格式为&#xff1a; plaintext IP地址 域名例如&#xff1a; plaintext 127.0.…...

Blender 入门教程(一):模型创建

一、前言 大家都知道&#xff0c;现在 AIGC 领域日新月异&#xff0c;今天 AI 大模型能生成粗糙的 3D 模型&#xff0c;明天就能做成商业级的 3D 模型&#xff0c;但是如果想要一些细节上也有的&#xff0c;还是需要自己手动对模型的布线进行调整&#xff0c;然后再蒙皮等等。…...

JVM对象头中的锁信息机制详解

JVM对象头中的锁信息机制详解 Java中的对象锁机制是高性能并发的基石&#xff0c;而这一切的底层实现都离不开对象头中的 Mark Word 字段。本文将系统梳理JVM对象头中锁信息的存储与演化机制&#xff0c;解析锁升级与批量重偏向优化原理&#xff0c;并通过JOL工具实战验证对象…...