一、优势。
(1)提高应用程序的响应速度。这对于开发图形界面的程序尤为重要,当一个操作耗时很长时,整 个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等的操作,而使用多线程技术可将耗时长的 操作置于一个新的线程,从而避免出现以上问题。
(2)使多CPU系统更加有效。当线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同 的CPU上。
(3)改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分, 这样有利于代码的理解和维护。
二、特点
(1)多线程程序的行为无法预期,当多次执行上述程序时,每次的运行结果都可能不同。
(2)多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关。
(3)多线程的切换可能发生在任何时刻、任何地点。
(4)由于多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的结果。
三、简单实例
单击“开始”按钮将启动数个工作线程(工作线程 数目由MAXSIZE宏决定),各个线程循环打印数字0~9,直到单击“停止”按钮终止所有线程为止。具体操作步骤如下:
(1)在头文件“threaddlg.h”中声明用于界面显示所需的控件,其具体代码如下:
#include <QDialog>
#include <QPushButton>
class ThreadDlg : public QDialog
{
Q_OBJECT
public:
ThreadDlg(QWidget *parent = 0);
~ThreadDlg();
private:
QPushButton *startBtn;
QPushButton *stopBtn;
QPushButton *quitBtn;
};
(2)在源文件“threaddlg.cpp”的构造函数中,完成各个控件的初始化工作,其具体代码如下:
#include "threaddlg.h"
#include <QHBoxLayout>
ThreadDlg::ThreadDlg(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(tr("线程"));
startBtn = new QPushButton(tr("开始"));
stopBtn = new QPushButton(tr("停止"));
quitBtn = new QPushButton(tr("退出"));
QHBoxLayout *mainLayout = new QHBoxLayout(this);
mainLayout->addWidget(startBtn);
mainLayout->addWidget(stopBtn);
mainLayout->addWidget(quitBtn);
}
(3)此时运行程序,界面显示如下图所示:
以上完成了界面的设计,下面的内容是具体的功能实现。
(1)在头文件“workthread.h”中,工作线程WorkThread类继承自QThread类。重新实现run()函数。其具体代码如下:
#include <QThread>
class WorkThread : public QThread
{
Q_OBJECT
public:
WorkThread();
protected:
void run();
};
(2)在源文件“workthread.cpp”中添加具体实现代码如下:
#include "workthread.h"
#include <QtDebug>
WorkThread::WorkThread()
{
}
run()函数实际上是一个死循环,它不停地打印数字0~9。为了显示效果明显,程序将每一个数字重复打印8次。
void WorkThread::run()
{
while(true)
{
for(int n=0;n<10;n++)
qDebug()<<n<<n<<n<<n<<n<<n<<n<<n;
}
}
(3)在头文件“threaddlg.h”中添加以下内容:
#include "workthread.h"
#define MAXSIZE 1 //MAXSIZE宏定义了线程的数目
public slots:
void slotStart(); //槽函数用于启动线程
void slotStop(); //槽函数用于终止线程
private:
WorkThread *workThread[MAXSIZE]; //(a) 指向工作线程(WorkThread)的私有指针数组workThread,记录了所启动的全部线程。
(4)在源文件“threaddlg.cpp”中添加以下内容。
在构造函数中添加如下代码:
connect(startBtn,SIGNAL(clicked()),this,SLOT(slotStart()));
connect(stopBtn,SIGNAL(clicked()),this,SLOT(slotStop()));
connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));
当用户单击“开始”按钮时,将调用槽函数slotStart()。这里使用两个循环,目的是使新建的线程尽可能同时开始执行,其具体实现代码如下:
void ThreadDlg::slotStart()
{
for(int i=0;i<MAXSIZE;i++)
{
workThread[i]=new WorkThread(); //创建指定数目的WorkThread线程,并将WorkThread实例的指针保存在指针数组workThread中。
}
for(int i=0;i<MAXSIZE;i++)
{
workThread[i]->start(); //(b) 调用QThread基类的start()函数,此函数将启动run()函数,从而使线程开始真正运行。
}
startBtn->setEnabled(false);
stopBtn->setEnabled(true);
}
当用户单击“停止”按钮时,将调用槽函数slotStop()。其具体实现代码如下:
void ThreadDlg::slotStop()
{
for(int i=0;i<MAXSIZE;i++)
{
workThread[i]->terminate();
workThread[i]->wait();
}
startBtn->setEnabled(true);
stopBtn->setEnabled(false);
}
(5)多线程简单实现结果如下图所示。
第1张图片是启动5个线程的运行结果,第2张图片是启动单一线程的运行结果。可以看出,单一线程的输出是顺序打印的,而多线程的输出结果则是乱序打印的,这正是多线程的一大特点。