前一篇我们提到了DataPool简单的有锁线程基类
今天我们优化一下他,变为无锁线程池
与前一版DataPool性能对比提升 40倍左右
无锁线程池库解析与优势分析
在高性能应用开发中,线程池是提升程序并发能力和资源利用率的重要工具。然而,传统的线程池实现往往依赖锁机制来保证线程安全,这在高并发场景下可能成为性能瓶颈。为了解决这一问题,本文将深入解析一个基于无锁设计的线程池实现——LocklessThreadPool,并探讨其在实际开发中的优势。
代码结构概述
LOCKLESS_THREAD_POOL_H 头文件主要包含以下几个部分:
- 数据结构定义:包括 pkt_bufferCache 和 LhtBufferCache,用于缓冲数据包。
- LhtLocklessThreadPool 类:管理线程池,负责任务的分发与执行。
- LocklessDataCache 类:实现无锁的数据缓存,支持高效的数据读写。
- LocklessThread 类:无锁线程基类,负责数据的接收与处理。
数据结构的定义
// 如果需要取数据包头的话用这个
struct pkt_bufferCache
{
struct pcap_pkthdr *header; // 数据包信息
uint32_t len;
char *pkt_data; // 数据包内容
struct pkt_bufferCache *next; // 下一个缓存
};
struct LhtBufferCache
{
uint32_t len;
char *pkt_data = nullptr; // 解包后的数据包内容
LhtBufferCache* next; // 下一个缓存
};
优点分析
- 链表结构:通过链表实现缓存的动态管理,避免了固定大小缓冲区的限制,提升了数据处理的灵活性。
- 结构简单:数据结构设计简洁,易于理解和维护。
LhtLocklessThreadPool 类
class LhtLocklessThreadPool : public QObject
{
Q_OBJECT
public:
static LhtLocklessThreadPool* instance(){
return m_instance;
}
explicit LhtLocklessThreadPool(QObject *parent = nullptr);
~LhtLocklessThreadPool();
void addWorker(QRunnable * work);
bool deleteWorker(QRunnable * work);
signals:
private:
int m_threadMaxCount;
QThreadPool m_pool;
static LhtLocklessThreadPool * m_instance;
};
LocklessDataCache 类
class LocklessDataCache
{
public:
LocklessDataCache();
void recv(char *data, uint32_t len);
// 缓存在函数内释放,需预先申请指针空间
bool readData(char *buffer, uint32_t &nbyte);
// 缓存由调用方释放,无拷贝过程
bool readDataZeroCopy(LhtBufferCache**);
void clearBuffer();
void setPoolLen(uint32_t nLen);
int getCurrentSize(){
return m_currentSize;
}
protected:
private:
// 清空数据的时候用
uint32_t poolSize = 1000;
std::atomic_int m_currentSize = 0;
LhtBufferCache* m_buffer_rd; // 数据包链表,写盘进程读指针
LhtBufferCache* m_buffer_wr; // 数据包链表,收到的包,网卡接收写指针
};
功能解析
无锁数据缓存:
通过原子变量 m_currentSize 和指针 m_buffer_rd、m_buffer_wr 实现无锁的读写操作,提升数据访问效率。
数据接收与读取:
recv 方法用于接收并缓存数据。
readData 和 readDataZeroCopy 方法用于读取缓存的数据,支持拷贝和零拷贝两种方式。
缓冲区管理:
setPoolLen 方法用于设置缓冲区的最大长度。
clearBuffer 方法用于清空缓存,释放资源。
LocklessThread 类
class LocklessThread : public QObject, public QRunnable
{
public:
LocklessThread(QObject *parent=0):QObject(parent)
{
containerLen = 1000;
packLen = 1296;
stopped = true;
}
~LocklessThread(){
stopped = true;
}
void recv(char* buf, int len){
m_lhtBufferCache.recv(buf, len);
} // 接收数据并缓存
virtual void stop()
{
stopped = true;
LhtLocklessThreadPool::instance()->deleteWorker(this);
} // 线程停止
__declspec(deprecated("需要预先设置pack len否则无法启动读线程"))
virtual void init(){
if(stopped){
LhtLocklessThreadPool::instance()->addWorker(this);
}
}
// 缓存的最大链表个数
void setPoolLen(unsigned int nLen){
containerLen = nLen;
m_lhtBufferCache.setPoolLen(nLen);
}
// 设置每个包大小
void setPackLen(unsigned int nLen){ packLen = nLen; }
// 设置是否启用零拷贝
void setUseZeroCopy(bool use){ m_useZeroCopy = use; }
int currentSize() { return m_lhtBufferCache.getCurrentSize(); }
void clearDataBuf(){
m_lhtBufferCache.clearBuffer();
}
protected:
void run()
{
if(packLen == -1 && (m_useZeroCopy == false)){
return;
}
char *data;
if(m_useZeroCopy == false){
data = new char[packLen];
}
uint32_t len;
LhtBufferCache* p_pkt_bufferNow = new LhtBufferCache;
stopped = false;
while(1) {
if(m_useZeroCopy){
if(!m_lhtBufferCache.readDataZeroCopy(&p_pkt_bufferNow)){
Sleep(1);
} else {
handleData(p_pkt_bufferNow->pkt_data, p_pkt_bufferNow->len);
delete []p_pkt_bufferNow->pkt_data;
p_pkt_bufferNow->pkt_data = nullptr;
delete p_pkt_bufferNow;
}
} else {
if(!m_lhtBufferCache.readData(data, len)){
Sleep(1);
} else {
handleData(data, len);
}
}
if (stopped)
{
break;
}
}
}
// 线程
virtual bool handleData(char* buf, int len)=0; // 处理队列中数据
private:
LocklessDataCache m_lhtBufferCache;
unsigned int containerLen;
unsigned int packLen;
volatile bool stopped;
bool m_useZeroCopy = 1;
};
优势总结
- 无锁设计:
通过原子操作和指针管理,避免了传统锁机制带来的性能开销,适用于高并发场景。
提升了数据读写的效率,减少了线程阻塞的可能性。
- 高效的线程池管理:
基于 QThreadPool,充分利用 Qt 提供的线程管理机制,简化了线程池的实现。
单例模式确保全局线程池的唯一性,避免了资源冲突。
- 灵活的数据缓存:
支持零拷贝和拷贝两种数据读取方式,满足不同性能和资源需求。
动态设置缓存大小和包大小,适应不同的数据量和处理需求。
- 与 Qt 的无缝集成:
继承自 QObject 和 QRunnable,便于在 Qt 应用中使用,提升了开发效率。
支持 Qt 的信号与槽机制,增强了组件之间的通信能力。
- 可扩展性与可维护性:
通过纯虚函数和模块化设计,允许开发者根据具体需求扩展功能,提升了代码的可维护性。
简洁的接口设计,易于理解和使用,降低了学习成本。
- 资源安全管理:
提供了动态的线程启动与停止机制,确保线程资源的有效管理和释放,避免资源泄漏。
通过链表结构和原子操作,确保数据缓存的线程安全性和一致性。
示例
#include "lockless_thread_pool.h"
#include <iostream>
// 自定义的数据处理线程
class MyDataHandler : public LocklessThread
{
protected:
bool handleData(char* buf, int len) override {
// 具体的数据处理逻辑
std::cout << "处理数据长度: " << len << std::endl;
// 处理完成后返回 true
return true;
}
};
int main()
{
// 初始化线程池
LhtLocklessThreadPool* pool = LhtLocklessThreadPool::instance();
// 创建数据处理线程
MyDataHandler* handler = new MyDataHandler();
handler->setPackLen(1024); // 设置每个包的大小
handler->setPoolLen(500); // 设置缓存大小
handler->setUseZeroCopy(true); // 启用零拷贝
handler->init(); // 启动线程
// 模拟接收数据
char data[1024] = {0};
handler->recv(data, sizeof(data));
// 等待一段时间
Sleep(1000);
// 停止线程
handler->stop();
delete handler;
return 0;
}
结语
LocklessThreadPool 通过无锁设计和高效的数据缓存机制,实现了高性能的线程管理与数据处理能力。结合 Qt 框架的强大功能,该线程池库不仅简化了多线程编程的复杂性,还提升了程序的并发性能和资源利用率。对于需要在高并发场景下进行高效数据处理的 C++ 开发者而言,LocklessThreadPool 提供了一种简洁而强大的解决方案。
通过本文的解析,希望您能够深入理解 LocklessThreadPool 的设计理念与实现细节,并在实际项目中灵活应用,进一步提升应用的性能与稳定性。
源代码
点点🌟🌟🌟🌟🌟🌟