Qt-多線程及簡單實例


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM