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
注:调试的时候,处理过程是异步的,它不是立即返回。