通常情况下,应用程序都是在一个线程中执行操作。但是,当调用一个耗时操作(例如,大批量I/O或大量矩阵变换等CPU密集操作)时,用户界面常常会冻结。而使用多线程可以解决这一问题。
多线程有以下几个优势:
1.提高应用程序响应速度。这对于图形界面开发的程序尤为重要,当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,避免以上问题。
2.使多CPU系统更加有效。当前线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同的CPU上。
3.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护。
多线程程序有以下几个特点:
1.多线程程序的行为无法预期,当多次执行程序时,每一次的结果都可能不同。
2.多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关。
3.多线程的切换可能发生在任何时刻、任何地点。
4.多线程对代码的敏感度高,对代码的细微修改都可能产生意想不到的结果。
基于以上这些特点,为了有效的使用线程,开发人员必须对其进行控制。
例子:
//开始按钮
void Widget::on_pushButton_clicked()
{
if(timer->isActive()!=true)
timer->start(100);
QThread::sleep(5);
}
前面介绍了定时器,就以这个例子吧。在上面的语句看出,timer->start(100)开始定时器,然后让该线程休眠5ms。结果发现窗口未响应,直接卡死。
解析:因为之前的例子全是单作业的。Sleep哪怕5ms,cpu也会去处理其他作业。也是因为 Qt 中所有界面都是在 UI 线程中(也被称为主线程,就是执行了QApplication::exec()的线程),在这个线程中执行耗时的操作(比如那个循环),就会阻塞 UI 线程,从而让界面停止响应。
那么就使用线程来试试,主窗口实现计时器的不断累加,将sleep放在线程中。如:线程中sleep(50),线程休眠50ms,而主窗口的线程的计时器也将要接近50ms。是否就可以说明窗口的线程与子线程是同时的呢?
Qt中线程的创建和使用:
解析:新建一个c++类(不需要有设计器的),MyThread作为线程类。此时要修改该线程类的基类,(由QWidget改为QThread).使用的线程处理函数是Qthread类中的虚函数(见下图)。
简单说,创建一个线程类,线程中的所有工作全部在该类中编写。接口:emit xxx。发出的信号就是该类的接口。便于其他类或窗口使用。
线程类:
class MyThread : public Qthread
{
protected:
void run();
signals:
void MySignal();
}
重写线程处理函数:
void MyThread::run()
{
QThread::sleep(50);
emit MySignal();
qDebug()<<"子线程ID:"<<QThread::currentThreadId();
}
主窗口类,启动定时器,并开启线程
void Widget::on_pushButton_clicked()
{
if(timer->isActive()!=true)
timer->start(1000);
myThread.start();
qDebug()<<"主线程ID:"<<QThread::currentThreadId();
}
//接受线程发出的信号
connect(&myThread,&MyThread::MySignal,
[=]()
{
qDebug()<<"over";
//timer->stop();
}
如图主线程的ID与子线程的ID不同,子线程休眠时间与主线程运行时间相近:
子线程关闭问题
当我们关闭窗口或关闭主线程,但是子线程仍然在运行。如下,我们的计时器已经暂停了,但是子线程还执行。
解决方法:手动调用Qthread类的槽函数。
注意:建议使用quit(),该函数会等待子线程执行完全部操作,再关闭。而terminate()调用后立即关闭线程,不管线程的后续操作,直接终止。
当窗口关闭时,关闭子线程
connect(this,&QWidget::destroyed,
[=]()
{
myThread.quit();
myThread.wait();
}
);
源代码:
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QWidget>
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
protected:
void run(); //线程处理函数
signals:
void MySignal(); //线程信号
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread(QObject *parent) :
QThread(parent)
{
}
//重写线程处理函数
void MyThread::run()
{
QThread::sleep(50);
emit MySignal(); //线程完成操作,发出信号
qDebug()<<"子线程ID:"<<QThread::currentThreadId();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTimer>
#include "mythread.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_pushButton_clicked(); //开始按钮
void on_pushButton_2_clicked(); //暂停按钮
private:
Ui::Widget *ui;
QTimer *timer; //定时器对象
MyThread myThread; //线程对象
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QThread>
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
timer=new QTimer(this);
//定时器槽函数
connect(timer,&QTimer::timeout,
[=]()
{
static int num=0;
ui->lcdNumber->display(num);
num++;
}
);
//线程信号槽函数
connect(&myThread,&MyThread::MySignal,
[=]()
{
qDebug()<<"over";
timer->stop();
}
);
//关闭窗口,并关闭子线程
connect(this,&QWidget::destroyed,
[=]()
{
myThread.quit();
myThread.wait();
}
);
}
Widget::~Widget()
{
delete ui;
}
//开始按钮,开始定时器和线程
void Widget::on_pushButton_clicked()
{
if(timer->isActive()!=true)
{
timer->start(1000);
}
myThread.start();
qDebug()<<"主线程ID:"<<QThread::currentThreadId();
}
void Widget::on_pushButton_2_clicked()
{
if(timer->isActive()==true)
{
timer->stop();
}
}