Qt多线程简述————moveToThread/QThread

1,086 阅读4分钟

文章目录

0 背景

因为项目需要处理TCP传来的特别快的数据,每秒600次,核算差不多1.6ms一次,如果单用一个主线程来处理特别容易卡死(因为虽然主线程接受数据很快,但是处理数据就会特别慢(相较与接受数据),就会导致来不及接受接下来的数据),因此需要用到多线程来处理。

以前都是单核计算机时,多线程其实都是并发的,也就是(微观上)多个线程轮换使用cpu,宏观上看起来是“同时”执行的,因此总体上效率并没有明显的提升。但是多核计算机时,可以并行(微观上同时)执行多个线程,执行效率会有明显提升。

1 moveToThread自动化管理线程【推荐】

处理数据的类:

class DealData:public QObject{
    Q_OBJECT
public slots:
    //处理数据函数
    void deal(QString);
signals:
    //把处理后的记过返回给主线程
    void resultReady(const QString);
};

主类(主线程):

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
signals:
    //激活线程的槽函数
    void activeThread(QString);
public slots:
    //处理返回后的结果
    void handleResult(QString);
private:
    //管理的线程 
    QThread dealDataThreadOne;
    //初始化线程
    void initDealDataThread();
};

事件处理

#ifdef Q_OS_LINUX
#include <pthread.h>
#endif

#ifdef Q_OS_WIN
#include <windows.h>
#endif

#include "widget.h"
#include "ui_widget.h"

#include<QDebug>
#include<QPushButton>
//构造函数
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //输出线程标示
    #ifdef Q_OS_LINUX
    qDebug() << pthread_self();
#endif

#ifdef Q_OS_WIN
    qDebug() << GetCurrentThreadId();
#endif

    //初始化线程
    initDealDataThread();
    //创建一个button,用于激活线程处理函数(也可以使用其他方式)
    QPushButton *pushButton;
    pushButton = new QPushButton(this);
    //设置按钮名
    pushButton->setObjectName(QString::fromUtf8("pushButton"));
    //设置大小
    pushButton->setGeometry(QRect(50, 50, 171, 61));
    //设置按钮中的文字
    pushButton->setText(QCoreApplication::translate("Widget", "\346\277\200\346\264\273\345\244\204\347\220\206\346\225\260\346\215\256\347\232\204\347\272\277\347\250\213", nullptr));
    //button点击时间激活
    connect(pushButton, &QPushButton::clicked, [=](){
        activeThread(QString("jiangxueHan"));
    });

}

Widget::~Widget()
{
    //退出线程
    dealDataThreadOne.quit();//停止事件循环
    dealDataThreadOne.wait();//阻塞知道线程结束
    delete ui;
}

void Widget::initDealDataThread()
{
    DealData* dealdata = new DealData; 
   qDebug()<<"主线程:"<<QThread::currentThreadId();
   
    dealdata->moveToThread(&dealDataThreadOne);
    //把数据处理交给其余线程
    connect(this, &Widget::activeThread, dealdata, &DealData::deal);
    //处理返回后的结果
    connect(dealdata, &DealData::resultReady, this, &Widget::handleResult);
        //线程结束后,销毁线程
    connect(&dealDataThreadOne, &QThread::finished, this, &DealData::deleteLater);
    //线程开始工作
    dealDataThreadOne.start();
    //下面的必须等线程启动后才能执行
        //设置堆栈大小,也可以实用化默认大小
    dealDataThreadOne.setStackSize(4*1024);//4kb大小
    //设置线程优先级
    /*
   QThread::InheritPriority (给创建的线程赋予目前线程同样的等级(默认情况下就为这个)) ,
   QThread::TimeCriticalPriority (尽可能频繁的调度),
    QThread::HighestPriority  >  QThread::HighPriority >  QThread::NormalPriority(操作系统默认的优先级)
   > QThread::LowPriority > QThread::LowestPriority  >QThread::IdlePriority (没有其他线程运行的时候才进行调度)
     */
    dealDataThreadOne.setPriority(QThread::InheritPriority);
    //将当前线程的执行权让给别的可执行线程(由操作系统决定)
   // dealDataThreadOne.yieldCurrentThread();
   
    //设置了一个布尔变量用来标记当前线程的终端状态
    //不设置时为fasle,设置后为true
    //用于用户自行判断中断时使用
    dealDataThreadOne.requestInterruption();
}

//处理主线程发送的数据
void DealData::deal(QString str)
{
    qDebug()<<"处理数据的线程:"<<QThread::currentThreadId();
    emit resultReady(str + ", hello!!");
}

//处理返回的结果
void Widget::handleResult(QString str)
{
    qDebug()<<"线程的中断标志位:"<<dealDataThreadOne.isInterruptionRequested();
    qDebug()<<"result:"<<str;
}

2 继承重写QThread

继承重写:

class Widget;
class MyThread: public QThread{
    //允许Widget类访问自己的私有变量
    friend class Widget;
private:
    volatile bool stopped = false;
protected:
    //线程处理函数
    void run() override;
public:
    //线程停止
    void stop();
};

重写事件:

//法一:
void MyThread::run()
{
    int i = 0;
    while(!stopped){
        qDebug()<<QString("%1 : %2").arg("处理线程:").arg(i);
        //睡眠函数
        usleep(1);//us
        msleep(1);//ms
        sleep(1);//s
        i++;
    }
}
//法二:
void MyThread::run()
{
    int i = 0;
    QTimer* timer = new QTimer;
    connect(timer, &QTimer::timeout, [&](){//引用捕获i
        if(!stopped){
            qDebug()<<"处理线程:"<<i;
            i++;
        }
    });
    timer->start(1000);
    exec();//开启事件循环,不然timer的信号发不出去
}

void MyThread::stop()
{
    stopped = true;
}

Widget主类:

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
};

Widget构造函数:


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化线程
    MyThread* myThread = new MyThread;
    //开始线程
    myThread->start();//调用run函数

    //创建一个button,用于激活线程处理函数(也可以使用其他方式)
    QPushButton *pushButton;
    pushButton = new QPushButton(this);
    //设置按钮名
    pushButton->setObjectName(QString::fromUtf8("pushButton"));
    //设置大小
    pushButton->setGeometry(QRect(50, 50, 171, 61));
    //设置按钮中的文字
    pushButton->setText(QCoreApplication::translate("Widget", "\346\277\200\346\264\273\345\244\204\347\220\206\346\225\260\346\215\256\347\232\204\347\272\277\347\250\213", nullptr));
    //button点击时间激活
    connect(pushButton, &QPushButton::clicked, [=](){
        qDebug()<<"myThread->stopped:"<<myThread->stopped;
          if(myThread->stopped == false){
              myThread->stop();
          }else{
              myThread->stopped = false;
              myThread->start();
          }
    });

}

3 Qt Concurrent

封装了QThread,方便使用。

Qt += concurrent
#include <QtConcurrent>

3.1 使用方法:

调用:

QtConcurrent::run(函数名, 参数名1, 参数名2,...);

使用lambda调用:

QFuture<void> future = QtConcurrent::run([=](){..Code..});

获得返回值:

QFuture<函数返回类型> future = QtConcurrent::run(函数名, 参数名1, 参数名2,...);

3.2 常见成员函数

QtConcurrent::map():直接操作容器中的每一项。
QtConcurrent::mapped():操作容器中的每一项,将处理结果返回一个新的容器,原容器不变。
QtConcurrent::mappedReduced():在 mapped() 的基础上将处理结果进一步传递给一个函数继续处理。
//效果同上,但侧重筛选元素
QtConcurrent::filter()
QtConcurrent::filtered()
QtConcurrent::filteredReduced()
//阻塞
QtConcurrent::blockingMap
QtConcurrent::blockingMapped
QtConcurrent::blockingMappedReduced
QtConcurrent::blockingfilter
QtConcurrent::blockingfiltered
QtConcurrent::blockingfilteredReduced

注:调试的时候,处理过程是异步的,它不是立即返回。