1. 概述
通常情况下,应用程序都是在一个线程中执行操作。但是,当调用一个耗时操作(例如,大批量I/O或大量矩阵变换等CPU密集操作)时,用户界面常常会冻结,而使用多线程可以解决这一问题
2. 优势
(1) 提高应用程序的响应速度。这对于开发图形界面尤为重要,当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,二使用多线程可将耗时长的操作置于一个新的线程,从而避免出现以上问题
(2) 使多CPU系统更加有效。当线程数不大于CPU数目时,操作系统可以调用不同的线程运行于不同的CPU上
(3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护
3. 特点
(1) 多线程程序的行为无法预期,当多次执行上述程序时,每次的运行结果都可能不同
(2) 多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关
(3) 多线程的切换可能发生在任何时刻、任何地点
(4) 由于多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的结果
4. 简单实例
单击"start"按钮将启动数个工作线程(工作线程数目由MAXSIZE宏决定),各个线程循环打印数字0~9,直到单击"stop"按钮终止所有线程为止
(1) threaddlg.h
#ifndef THREADDLG_H
#define THREADDLG_H
#include <QDialog>
#include <QPushButton>
#include <workthread.h>
#define MAXSIZE 1
class ThreadDlg : public QDialog
{
Q_OBJECT
public:
//explicit关键字禁止构造函数隐式类型转换(C++ 11新特性)
explicit ThreadDlg(QWidget *parent = nullptr);
signals:
public slots:
void slotStart(); //槽函数用于启动线程
void slotStop(); //槽函数用于终止线程
private:
QPushButton* m_startButton;
QPushButton* m_stopButton;
QPushButton* m_quitButton;
WorkThread* workThread[MAXSIZE]; //记录了所启动的全部线程
};
#endif // THREADDLG_H
(2) threaddlg.cpp
#include "threaddlg.h"
#include <QHBoxLayout>
ThreadDlg::ThreadDlg(QWidget *parent) : QDialog(parent)
{
m_startButton = new QPushButton(tr("start"));
m_stopButton = new QPushButton(tr("stop"));
m_quitButton = new QPushButton(tr("quit"));
QHBoxLayout* mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(m_startButton);
mainLayout->addWidget(m_stopButton);
mainLayout->addWidget(m_quitButton);
connect(m_startButton, SIGNAL(clicked()), this, SLOT(slotStart()));
connect(m_stopButton, SIGNAL(clicked()), this, SLOT(slotStop()));
connect(m_quitButton, SIGNAL(clicked()), this, SLOT(close()));
}
void ThreadDlg::slotStart()
{
//使用两个for循环,目的是使新建的线程尽可能同时开始执行
for(int i = 0; i < MAXSIZE; ++i)
{
workThread[i] = new WorkThread();
}
for(int i = 0; i < MAXSIZE; ++i)
{
//调用QThread基类的start(),此函数将启动WorkThread类的run(),从而使线程开始真正运行
workThread[i]->start();
}
m_startButton->setEnabled(false);
m_stopButton->setEnabled(true);
}
void ThreadDlg::slotStop()
{
for(int i = 0; i < MAXSIZE; ++i)
{
//调用QThread基类的terminate(),依次终止保存在workThread[]数组中的WorkThread类实例
workThread[i]->terminate();
//terminate()并不会立刻终止这个线程,该线程何时终止取决于操作系统的调度策略,因此,
//QThread基类的wait()使线程阻塞等待直到退出或超时
workThread[i]->wait();
}
m_startButton->setEnabled(true);
m_stopButton->setEnabled(false);
}
(3) workthread.h
#ifndef WORKTHREAD_H
#define WORKTHREAD_H
#include <QThread>
class WorkThread : public QThread
{
Q_OBJECT
public:
WorkThread();
protected:
void run();
};
#endif // WORKTHREAD_H
(4) workthread.cpp
#include "workthread.h"
#include <QDebug>
WorkThread::WorkThread()
{
}
void WorkThread::run()
{
while(true)
{
for (int i = 0; i < 10; ++i)
{
//此处不使用printf()的原因:线程将因为调用printf()而持有一个控制I/O的锁(lock),多个线程同时调用printf()在某些
//情况下将造成控制台输出阻塞,而使用qDebug()作为控制台输出则不会出现上述问题
qDebug() << i << i << i << i << i << i << i << i;
}
}
}
(5) 运行结果,左边的图是启动5个线程的运行结果,右边的图是启动单一线程的运行结果。可以看出,单一线程的输出是顺序打印的,而多线程的输出结果是乱序打印的,这正是多线程的一大特点