Qt-多執行緒及簡單例項
阿新 • • 發佈:2022-03-22
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個執行緒的執行結果,右邊的圖是啟動單一執行緒的執行結果。可以看出,單一執行緒的輸出是順序列印的,而多執行緒的輸出結果是亂序列印的,這正是多執行緒的一大特點