目录
通常情况下,应用程序都是在一个线程中执行操作,但当调用一个耗时操作时,用户界面常常会冻结,而使用多线程可以解决这一问题。多线程有以下几个优势:
在Qt中使用QThread 来管理线程。下面来看一个简单的例子:
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myTimer=new QTimer(this);
//只要定时器启动,自动触发timeout()
connect(myTimer,&QTimer::timeout,this,&MyWidget::dealTimeout);
}
void MyWidget::dealTimeout()
{
static int i=0;
i++;
//设置lcd的值
ui->lcdNumber->display(i);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_pushButton_clicked()
{
//如果定时器没有工作
if(myTimer->isActive()==false)
{
myTimer->start(100);
}
//非常复杂的数据处理,耗时较长
QThread::sleep(5);
//处理完数据后,关闭定时器
//myTimer->stop();
qDebug() << "over";
}
主界面有一个用于显示时间的 LCD 数字面板还有一个用于启动任务的按钮。程序的目的是用户点击按钮,开始一个非常耗时的运算(程序中我们以一个 2000000000 次的循环来替代这个非常耗时的工作,在真实的程序中,这可能是一个网络访问,可能是需要复制一个很大的文件或者其它任务),同时 LCD 开始显示逝去的毫秒数。毫秒数通过一个计时器QTimer进行更新。计算完成后,计时器停止。这是一个很简单的应用,也看不出有任何问题。但是当我们开始运行程序时,问题就来了:点击按钮之后,程序界面直接停止响应,直到循环结束才开始重新更新。
但我们注释掉 myTimer->stop(); 语句时,LED显示会在5s后出现
让线程睡眠5s:
//mythread.cpp
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
//非常复杂的数据处理,耗时较长
sleep(5);
emit isDone();
}
//mywidget.cpp
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myTimer=new QTimer(this);
//只要定时器启动,自动触发timeout()
connect(myTimer,&QTimer::timeout,this,&MyWidget::dealTimeout);
//分配空间
thread=new MyThread(this);
connect(thread,&MyThread::isDone,this,&MyWidget::dealDone);
//当按窗口右上角x时,窗口触发destroyed()
connect(this,&MyWidget::destroyed,this,&MyWidget::stopThread);
}
void MyWidget::stopThread()
{
//停止线程
thread->quit();
//等待线程处理完手头工作
thread->wait();
}
void MyWidget::dealDone()
{
qDebug() << "it is over";
myTimer->stop(); //关闭定时器
}
void MyWidget::dealTimeout()
{
static int i=0;
i++;
//设置lcd的值
ui->lcdNumber->display(i);
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::on_pushButton_clicked()
{
//如果定时器没有工作
if(myTimer->isActive()==false)
{
myTimer->start(100);
}
// //非常复杂的数据处理,耗时较长
// QThread::sleep(5);
// //处理完数据后,关闭定时器
// //myTimer->stop();
// qDebug() << "over";
//启动线程,处理数据
thread->start();
}
结果如下:
线程到5s时就会停止
主线程和子线程互不占用和阻塞,可以随时开始和暂停,并且退出窗口时不会占内存
mythread.cpp
MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop=false;
}
void MyThread::myTimeout()
{
while(!isStop)
{
QThread::sleep(1);
emit mySignal();
//QMessageBox::about(NULL);
qDebug() << "子线程号:" << QThread::currentThread();
if(isStop)
{
break;
}
}
}
void MyThread::setFlag(bool flag)
{
isStop=flag;
}
mywidget.cpp
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//动态分配空间,不能指定父对象
myT=new MyThread;
//创建子线程
thread=new QThread(this);
//把自定义的线程加入到子线程中,关联起来
myT->moveToThread(thread);
connect(myT,&MyThread::mySignal,this,&MyWidget::dealSignal);
qDebug() << "主线程号:" << QThread::currentThread();
connect(this,&MyWidget::startThread,myT,&MyThread::myTimeout);
connect(this,&MyWidget::destroyed,this,&MyWidget::dealClose);
//线程处理函数内部,不允许操作图形界面
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::dealClose()
{
on_buttonStop_clicked();
}
void MyWidget::dealSignal()
{
static int i=0;
i++;
ui->lcdNumber->display(i);
}
void MyWidget::on_buttonStart_clicked()
{
if(thread->isRunning()==true)
{
return;
}
//启动线程,但是没有启动线程处理函数
thread->start();
myT->setFlag(false);
//不能直接调用线程处理函数;直接调用,导致线程处理函数和主线程是在同一个线程
//myT->myTimeout();
//只能通过 signal - slot 方式调用
emit startThread();
}
void MyWidget::on_buttonStop_clicked()
{
if(thread->isRunning()==false)
{
return;
}
myT->setFlag(true);
thread->quit();
thread->wait();
}
结果如下:
总结:
* 队列连接:槽函数在接受者所在线程执行。
* 直接连接:槽函数在发送者所在线程执行。
* 自动连接:二者不在同一线程时,等同于队列连接
在窗口中有一个按钮,当点击按钮之后,在线程中随机
绘制一张图片,然后将给绘制好的图片显示到当前窗口中。
//widget.cpp
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//自定义类对象,分配空间,不可以指定对象
myT=new MyThread;
//创建子线程
thread=new QThread(this);
//把自定义模块添加到子线程
myT->moveToThread(thread);
//启动子线程,但是并没有启动线程处理函数
thread->start();
//线程处理函数必须通过 signal - slot调用
connect(ui->pushButton,&QPushButton::pressed,myT,&MyThread::drawImage);
connect(myT,&MyThread::updateImage,this,&Widget::getImage);
connect(this,&Widget::destroyed,this,&Widget::dealClose);
}
void Widget::dealClose()
{
//退出子线程,回收资源
thread->quit();
thread->wait();
delete myT;
}
void Widget::getImage(QImage temp)
{
image=temp;
update(); //更新窗口,间接调用paintEvent()
}
void Widget::paintEvent(QPaintEvent *)
{
//创建画家,指定绘图设备为窗口
QPainter p(this);
p.drawImage(50,50,image);
}
//mythread.cpp
void MyThread::drawImage()
{
//定义QImage绘图设备
QImage image(500,500,QImage::Format_ARGB32);
//定义画家,指定绘图设备
QPainter p(&image);
//设定画笔对象
QPen pen;
pen.setWidth(5);
//把画笔交给画家
p.setPen(pen);
//定义画刷
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::green);
//把画刷交给画家
p.setBrush(brush);
//定义5个点
QPoint a[] =
{
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500),
QPoint(qrand()%500,qrand()%500)
};
p.drawPolygon(a,5);
//通过信号发送图片
emit updateImage(image);
}
结果如下:
按下按钮,可以随机生成一个图片