C++回调函数与多线程联动使用

问题

在业务场景中,会遇到这样的情况,当前类需要运行一个函数,这个函数会被使用多次或者耗时非常长,这时候我们希望把它放到子线程里面运行,主线程只需要知道它的运行状态即可(类似于进度条通知)。但我们又不希望更改该类任何地方(该类有可能是同事提供的,原则上只加不改最好)。

具体设置

首先我们创建一个常规类MainWindow,里面具有我们需要使用的方法,这里图简单,直接创建一个QT window工程。

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void test(QString* s);
    QString* s = new QString("sss");
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

MainWindow.cpp

#include "qdebug.h"
#include <iostream>
#include <functional>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    s = new QString("sss");
     qDebug()<<"变化前"<<*s;
    test(s);
     qDebug()<<"变化后"<<*s;
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::test(QString* s)
{
    qDebug()<<"test::run"<<QThread::currentThreadId();
    qDebug()<<"test::run"<<*s;
    *s += "qqq";
}

简单测试,运行结果:

MyThread::run 0x324c
变化前 "sss"
test::run 0x324c
test::run "sss"
变化后 "sssqqq"

现在我们希望使用子线程回调函数来调用我们的test函数,也就是把它移到子线程中运行,为了使代码减少耦合度,我们不在本类中修改。创建一个线程类ThreadTask。

其中包含预设的回调函数定义。

ThreadTask.h

//普通回调 普通回调需要保证调用的函数需要为static或者本类能够实际找到的地址。
typedef void (*mCallback)(QString* s); // 回调
//这里有一个重点,如果你是不同类之间调用回调的话,需要添加类名声明。
typedef void (MainWindow::*mCallback)(QString* s); // 回调

#ifndef THREADTASK_H
#define THREADTASK_H

#include <qthread.h>
#include <qdebug.h>
#include "mainwindow.h"


typedef void (MainWindow::*mCallback)(QString* s); // 回调
class ThreadTask :public QThread
{
    Q_OBJECT
public:
    //回调对象
    MainWindow *_obj;
    enum STATUS{
        running,
        waiting,
    };
    //回调函数参数
    //参数存储
    QString* s;
    //线程状态
    STATUS status = waiting;
    //回调函数存储
    mCallback callback_function;
    //构造
    ThreadTask();
    //设置回调函数
    void set_callback(MainWindow* obj,mCallback callback, QString * str);
protected:
    void run();//多线程执行的内容将通过重新该虚函数实现
signals:
    void over();
};

#endif // THREADTASK_H

ThreadTask.cpp

#include "threadtask.h"
#include "qdebug.h"
ThreadTask::ThreadTask()
{
}
void ThreadTask::set_callback(MainWindow* obj, mCallback callback,QString* str)
{
    s = str;
    qDebug()<<"set_callback"<<*s;
    _obj = obj;
    callback_function =callback;
}
void ThreadTask::run()
{
    status =running;
    qDebug()<<"ThreadTask::run"<<QThread::currentThreadId();
    (_obj->*callback_function)(s);
    emit over();
    status =waiting;
}

编写完子线程回调后,我们修改一下最初的函数调用形式。

#include "qdebug.h"
#include "Public_Define.h"
#include <iostream>
#include <functional>
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ThreadTask * thread =new ThreadTask();
    connect(thread,&ThreadTask::over,this,[=](){
        qDebug()<<"变化后"<<*s;
    });
    qDebug()<<"MyThread::run"<<QThread::currentThreadId();



    qDebug()<<"变化前"<<*s;
    mCallback call(&test);
    thread->set_callback(this,call,s);
    thread->start();
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::test(QString* s)
{
    qDebug()<<"test::run"<<QThread::currentThreadId();
    qDebug()<<"test::run"<<*s;
    *s += "qqq";
}

可以发现我们并没有修改到原类的大部分内容,只修改了test函数的调用方式。看看运行效果。

MyThread::run 0x6d24
变化前 "sss"
set_callback "sss"
ThreadTask::run 0x52a0
test::run 0x52a0
test::run "sss"
变化后 "sssqqq"

可以看到我们把test任务成功以回调的形式在子线程中运行。

总结

通过创建一个子线程类并附加回调定义,我们很轻松地实现了在任意一个类中对特定方法进行异步调用(多线程调用),从而达到提高运行效率、防卡顿异步处理等效果。

后续希望能够将子线程类作为一个模板类,提高它的健壮性,让其能够作为一个通用方法使用。

相关推荐
景天科技苑35 分钟前
【Go】Go语言中延迟函数、函数数据的类型、匿名函数、闭包等高阶函数用法与应用实战
后端·golang·回调函数·defer·匿名函数·闭包·go函数数据类型
敲代码的奥豆36 分钟前
C++:日期类的实现
开发语言·c++
亿牛云爬虫专家1 小时前
如何通过subprocess在数据采集中执行外部命令 —以微博为例
爬虫·python·数据采集·多线程·代理ip·subprocess·微博
You can do more1 小时前
Qt Model/View之代理
qt
redcocal2 小时前
地平线内推码 kbrfck
c++·嵌入式硬件·mcu·算法·fpga开发·求职招聘
归去来兮★2 小时前
c++面向对象
java·开发语言·c++
Trouvaille ~2 小时前
【C++篇】C++类与对象深度解析(四):初始化列表、类型转换与static成员详解
c++·类型转换·类和对象·面向对象编程·static·初始化列表·开发者指南
杨~friendship2 小时前
Ubuntu上使用qt和opencv显示图像
linux·开发语言·c++·qt·opencv·ubuntu
CS小麻瓜2 小时前
Web植物管理系统-下位机部分
c++·嵌入式硬件·湖南大学
界面开发小八哥3 小时前
「Qt Widget中文示例指南」如何实现一个系统托盘图标?(二)
开发语言·c++·qt·用户界面