2.无锁线程(升级版双指针) 性能更高

前一篇我们提到了DataPool简单的有锁线程基类

今天我们优化一下他,变为无锁线程池

前一版DataPool性能对比提升 40倍左右

在这里插入图片描述

无锁线程池库解析与优势分析

在高性能应用开发中,线程池是提升程序并发能力和资源利用率的重要工具。然而,传统的线程池实现往往依赖锁机制来保证线程安全,这在高并发场景下可能成为性能瓶颈。为了解决这一问题,本文将深入解析一个基于无锁设计的线程池实现——LocklessThreadPool,并探讨其在实际开发中的优势。

代码结构概述
LOCKLESS_THREAD_POOL_H 头文件主要包含以下几个部分:

  1. 数据结构定义:包括 pkt_bufferCache 和 LhtBufferCache,用于缓冲数据包。
  2. LhtLocklessThreadPool 类:管理线程池,负责任务的分发与执行。
  3. LocklessDataCache 类:实现无锁的数据缓存,支持高效的数据读写。
  4. 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; // 下一个缓存
};

优点分析

  1. 链表结构:通过链表实现缓存的动态管理,避免了固定大小缓冲区的限制,提升了数据处理的灵活性。
  2. 结构简单:数据结构设计简洁,易于理解和维护。

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;
};

优势总结

  1. 无锁设计:

通过原子操作和指针管理,避免了传统锁机制带来的性能开销,适用于高并发场景。
提升了数据读写的效率,减少了线程阻塞的可能性。

  1. 高效的线程池管理:

基于 QThreadPool,充分利用 Qt 提供的线程管理机制,简化了线程池的实现。
单例模式确保全局线程池的唯一性,避免了资源冲突。

  1. 灵活的数据缓存:

支持零拷贝和拷贝两种数据读取方式,满足不同性能和资源需求。
动态设置缓存大小和包大小,适应不同的数据量和处理需求。

  1. 与 Qt 的无缝集成:

继承自 QObject 和 QRunnable,便于在 Qt 应用中使用,提升了开发效率。
支持 Qt 的信号与槽机制,增强了组件之间的通信能力。

  1. 可扩展性与可维护性:

通过纯虚函数和模块化设计,允许开发者根据具体需求扩展功能,提升了代码的可维护性。
简洁的接口设计,易于理解和使用,降低了学习成本。

  1. 资源安全管理:

提供了动态的线程启动与停止机制,确保线程资源的有效管理和释放,避免资源泄漏。
通过链表结构和原子操作,确保数据缓存的线程安全性和一致性。

示例

#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 的设计理念与实现细节,并在实际项目中灵活应用,进一步提升应用的性能与稳定性。

源代码

点点🌟🌟🌟🌟🌟🌟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值