UI程序中经常有一些耗时的操作要处理,如果直接在UI线程中执行,整个界面都会卡住,出现无响应状态。
如果每次操作都生成一个线程类,而且相关数据都要传递过去,可能处理起来会比较麻烦!
因此本篇文章的初衷就是想简化这种情况。

qt多线程编程(Qt多线程功能简化方案)(1)

仿照MFC方式解决

最开始我想到的就是按照MFC创建线程的方式进行解决。
通过类的 static 成员函数作为函数指针,通过QThread的run函数进行回调。
思路如下:

class Form : public QWidget
{
    Q_OBJECT
public: 
    Form(QWidget *parent=0) : QWidget(parent)
    {
        ...
        m_thread.setCallBack(run, this);
        m_thread.start();
    }
...
private:
    void static run(QObject* obj)
    {
        Form*frm = (Form*) obj;
        frm->todo();
    }

    void todo()
    {
         // 添加需要做的事情
    }
...
private:
    CThread m_thread;
}
#ifndef CTHREAD_H
#define CTHREAD_H

#include <QThread>

// 函数指针定义,用于回调的;QObject *即为传递过来的 Form 的 this 对象指针
typedef void (*pFuncPointer)(QObject *);   

class CThread : public QThread
{
public:
    CThread(QObject *obj=0):QThread(obj) {m_obj = obj;}
    CThread(pFuncPointer func, QObject *obj):QThread(obj) {m_func = func; m_obj = obj;}

    void setCallBack(pFuncPointer func, QObject *obj) {m_func = func; m_obj = obj;}
    void setObj(QObject *obj) {m_obj = obj;}
    void setFun(pFuncPointer func) {m_func = func;}

private:
    void run(){m_func(m_obj);}  // 回调函数,将 this 传递回去

private:
    pFuncPointer m_func;
    QObject *m_obj;
};

#endif // CTHREAD_H

更进一步

由于我实在太懒了,觉得定义按照MFC那样的方式定义 static 成员函数 实在太麻烦了;
想直接一步到位,如果这样可以通过传递类的函数指针,这样就可以简化成下面这样!
思路如下:

class Form : public QWidget
{
    Q_OBJECT
public: 
    Form(QWidget *parent=0) : QWidget(parent)
    {
        ...
        m_thread.setCallBack(&MainForm::todo, this);  // 成员函数指针
        m_thread.start();
    }
...
private:
    void todo()
    {
         // 添加需要做的事情
    }
...
private:
    CThread<Form> m_thread;
}
#ifndef CTHREAD_H
#define CTHREAD_H

#include <QThread>

template <typename T>                    // 模板
class CThread : public QThread
{
public:
    typedef void (T::*pFuncPointer)();   // 模板函数指针
    CThread(T *obj=0):QThread(obj) {m_obj = obj;}
    CThread(pFuncPointer func, T *obj):QThread(obj) {m_func = func; m_obj = obj;}

    void setCallBack(pFuncPointer func, T *obj) {m_func = func; m_obj = obj;}
    void setObj(T *obj) {m_obj = obj;}
    void setFun(pFuncPointer func) {m_func = func;}

private:
    void run(){(m_obj->*m_func)();}  // 回调,这里的写法需要注意

private:
    pFuncPointer m_func;
    T *m_obj;
};

#endif // CTHREAD_H

需要注意的地方

由于 todo 函数虽然是 Form 的成员函数,但是它是被另一个线程所调用的,所以它不属于UI线程。
在更新界面相关,或者需要更新界面信息时,可以在 Form 里面定义信号和对应的槽;
而且连接的时候,最好明确指明最后一个参数为Qt::QueuedConnection

总结

我的砖就抛到这里,希望对你们有用。
接下来的路就请各位小伙伴们自己走了!