【C++/Qt shared_ptr 与 线程池】合作使用案例

以下是一个结合 std::shared_ptr 和 Qt 线程池(QThreadPool)的完整案例,展示了如何在多线程任务中安全管理资源,避免内存泄漏。


案例场景

  • 任务目标:在后台线程中处理一个耗时的图像检测任务,任务对象通过 std::shared_ptr 管理。
  • 关键需求
    • 确保任务对象在任务完成后自动释放。
    • 支持跨线程信号槽通信,传递处理结果。
    • 避免线程池与智能指针所有权冲突。

完整代码

1. 自定义任务类(继承自 QRunnable
// MyTask.h
#pragma once
#include <QRunnable>
#include <QObject>
#include <memory>
#include <QImage>

class MyTask : public QObject, public QRunnable
{
    Q_OBJECT
public:
    MyTask(const QImage& inputImage);
    
    // 任务执行入口
    void run() override;

    // 设置任务完成后回调(通过信号)
    void setResultCallback(std::function<void(const QImage&)> callback);

signals:
    // 任务完成信号,传递处理后的图像
    void taskFinished(const QImage& result);

private:
    QImage m_inputImage;
    std::function<void(const QImage&)> m_callback;
};
// MyTask.cpp
#include "MyTask.h"
#include <QDebug>

MyTask::MyTask(const QImage& inputImage) 
    : m_inputImage(inputImage)
{
    // 禁用 QRunnable 的自动删除
    setAutoDelete(false);
}

void MyTask::run()
{
    qDebug() << "Task started in thread:" << QThread::currentThreadId();
    
    // 模拟耗时操作(例如图像处理)
    QImage processedImage = m_inputImage.mirrored(true, false);
    
    // 发送完成信号(跨线程)
    emit taskFinished(processedImage);
    
    // 或者调用回调函数
    if (m_callback) {
        m_callback(processedImage);
    }
}

void MyTask::setResultCallback(std::function<void(const QImage&)> callback)
{
    m_callback = callback;
}

2. 主窗口类(使用线程池和 shared_ptr
// MainWindow.h
#pragma once
#include <QMainWindow>
#include <QThreadPool>
#include <memory>

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    // 处理任务完成信号
    void handleTaskFinished(const QImage& result);

private:
    QThreadPool* m_threadPool;
};
// MainWindow.cpp
#include "MainWindow.h"
#include "MyTask.h"
#include <QPushButton>
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), m_threadPool(new QThreadPool(this))
{
    // 设置线程池最大线程数
    m_threadPool->setMaxThreadCount(4);

    // 创建一个测试按钮,点击后提交任务
    QPushButton* btn = new QPushButton("Run Task", this);
    connect(btn, &QPushButton::clicked, [this]() {
        // 1. 创建任务对象并用 shared_ptr 管理
        QImage inputImage(800, 600, QImage::Format_RGB32);
        inputImage.fill(Qt::green);
        
        std::shared_ptr<MyTask> task = std::make_shared<MyTask>(inputImage);

        // 2. 连接任务完成信号到主线程的槽
        connect(task.get(), &MyTask::taskFinished, 
                this, &MainWindow::handleTaskFinished, 
                Qt::QueuedConnection); // 确保跨线程安全

        // 3. 提交任务到线程池(传递原始指针,但所有权由 shared_ptr 控制)
        m_threadPool->start(task.get());

        // 4. 使用 Lambda 捕获 shared_ptr,确保任务完成后释放资源
        task->setResultCallback([task](const QImage& result) {
            qDebug() << "Task callback executed. Ref count:" << task.use_count();
        });
    });
}

void MainWindow::handleTaskFinished(const QImage& result)
{
    qDebug() << "Task finished. Result size:" << result.size();
}

MainWindow::~MainWindow()
{
    // 等待所有任务完成
    m_threadPool->waitForDone();
}

3. 主函数
// main.cpp
#include "MainWindow.h"
#include <QApplication>

int main(int argc char *argv[])
{
    QApplication a(argc, argv);

    // 注册自定义类型(若需要传递复杂类型)
    // qRegisterMetaType<MyData>("MyData");

    MainWindow w;
    w.show();
    return a.exec();
}

关键机制解释

  1. 禁用自动删除

    setAutoDelete(false); // 在 MyTask 构造函数中
    
    • 阻止 QThreadPool 自动删除任务对象,避免与 shared_ptr 冲突。
  2. 通过 shared_ptr 管理生命周期

    std::shared_ptr<MyTask> task = std::make_shared<MyTask>(inputImage);
    
    • shared_ptr 确保任务对象在最后一个引用消失时自动释放。
  3. 信号槽跨线程通信

    connect(task.get(), &MyTask::taskFinished, 
            this, &MainWindow::handleTaskFinished, 
            Qt::QueuedConnection);
    
    • 使用 Qt::QueuedConnection 确保信号跨线程安全传递。
  4. Lambda 捕获 shared_ptr

    task->setResultCallback([task](const QImage& result) {
        qDebug() << "Ref count:" << task.use_count();
    });
    
    • Lambda 表达式捕获 task 会递增引用计数,确保任务执行期间对象存活。

运行流程

  1. 用户点击按钮,创建任务并用 shared_ptr 管理。
  2. 任务被提交到线程池,线程池调用 run() 执行耗时操作。
  3. 任务完成后,发送 taskFinished 信号或调用回调。
  4. 主线程接收结果并处理。
  5. 当所有引用(shared_ptr)释放后,任务对象自动销毁。

注意事项

  1. 线程安全设计
    • 如果任务内部访问共享数据,需使用 QMutexQReadWriteLock 保护。
  2. 避免循环引用
    • 如果任务对象持有指向主窗口的指针,需使用原始指针或 weak_ptr,避免 shared_ptr 循环引用导致内存泄漏。
  3. 性能优化
    • 如果频繁创建任务,可复用任务对象或使用对象池减少内存分配开销。

通过此案例,您可以安全地在 Qt 线程池中使用 std::shared_ptr,确保资源生命周期正确管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flos chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值