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個線程的運行結果,右邊的圖是啟動單一線程的運行結果。可以看出,單一線程的輸出是順序打印的,而多線程的輸出結果是亂序打印的,這正是多線程的一大特點