欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解Qt中線程的實(shí)際應(yīng)用

 更新時(shí)間:2023年03月10日 10:06:13   作者:音視頻開(kāi)發(fā)老舅  
為了讓程序盡快響應(yīng)用戶操作,在開(kāi)發(fā)應(yīng)用程序時(shí)經(jīng)常會(huì)使用到線程。這篇文章就來(lái)和大家介紹一下Qt中線程的實(shí)際應(yīng)用,感興趣的小伙伴可以了解一下

為了讓程序盡快響應(yīng)用戶操作,在開(kāi)發(fā)應(yīng)用程序時(shí)經(jīng)常會(huì)使用到線程。對(duì)于耗時(shí)操作如果不使用線程,UI界面將會(huì)長(zhǎng)時(shí)間處于停滯狀態(tài),這種情況是用戶非常不愿意看到的,我們可以用線程來(lái)解決這個(gè)問(wèn)題。

大多數(shù)情況下,多線程耗時(shí)操作會(huì)與UI進(jìn)行交互,比如:顯示進(jìn)度、加載等待。。。讓用戶明確知道目前的狀態(tài),并對(duì)結(jié)果有一個(gè)直觀的預(yù)期,甚至有趣巧妙的設(shè)計(jì),能讓用戶愛(ài)上等待,把等待看成一件很美好的事。

1、多線程操作UI界面的示例

下面,是一個(gè)使用多線程操作UI界面的示例 - 更新進(jìn)度條,采用子類化QThread的方式。與此同時(shí),分享在此過(guò)程中有可能遇到的問(wèn)題及解決方法。

首先創(chuàng)建QtGui應(yīng)用,工程名稱為“myThreadBar”,類名選擇“QMainWindow”,其他選項(xiàng)保持默認(rèn)即可。再添加一個(gè)名稱為WorkerThread的頭文件,定義一個(gè)WorkerThread類,讓其繼承自QThread,并重寫(xiě)run()函數(shù),修改workerthread.h文件如下:

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
 
#include <QThread>
#include <QDebug>
 
class WorkerThread : public QThread
{
    Q_OBJECT
 
public:
    explicit WorkerThread(QObject *parent = 0)
        : QThread(parent)
    {
        qDebug() << "Worker Thread : " << QThread::currentThreadId();
    }
 
protected:
    virtual void run() Q_DECL_OVERRIDE
    {
        qDebug() << "Worker Run Thread : " << QThread::currentThreadId();
        int nValue = 0;
        while (nValue < 100)
        {
            // 休眠50毫秒
            msleep(50);
            ++nValue;
 
            // 準(zhǔn)備更新
            emit resultReady(nValue);
        }
    }
 
signals:
    void resultReady(int value);
};
 
#endif // WORKERTHREAD_H

通過(guò)在run()函數(shù)中調(diào)用msleep(50),線程會(huì)每隔50毫秒讓當(dāng)前的進(jìn)度值加1,然后發(fā)射一個(gè)resultReady()信號(hào),其余時(shí)間什么都不做。在這段空閑時(shí)間,線程不占用任何的系統(tǒng)資源。當(dāng)休眠時(shí)間結(jié)束,線程就會(huì)獲得CPU時(shí)鐘,將繼續(xù)執(zhí)行它的指令。

再在mainwindow.ui上添加一個(gè)按鈕和進(jìn)度條部件,然后mainwindow.h修改如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include "workerthread.h"
 
namespace Ui {
class MainWindow;
}
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
 
private slots:
    // 更新進(jìn)度
    void handleResults(int value);
 
    // 開(kāi)啟線程
    void startThread();
 
private:
    Ui::MainWindow *ui;
 
    WorkerThread m_workerThread;
};
 
#endif // MAINWINDOW_H

然后mainwindow.cpp修改如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
        
    qDebug() << "Main Thread : " << QThread::currentThreadId();        
 
    // 連接信號(hào)槽
    this->connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(startThread()));
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::handleResults(int value)
{
    qDebug() << "Handle Thread : " << QThread::currentThreadId();
    ui->progressBar->setValue(value);
}
 
void MainWindow::startThread()
{
    WorkerThread *workerThread = new WorkerThread(this);
    this->connect(workerThread, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
    // 線程結(jié)束后,自動(dòng)銷毀
    this->connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
    workerThread->start();
}

由于信號(hào)與槽連接類型默認(rèn)為“Qt::AutoConnection”,在這里相當(dāng)于“Qt::QueuedConnection”。也就是說(shuō),槽函數(shù)在接收者的線程(主線程)中執(zhí)行。

執(zhí)行程序,“應(yīng)用程序輸出”窗口輸出如下:

Main Thread :  0x3140
Worker Thread :  0x3140
Worker Run Thread :  0x2588
Handle Thread :  0x3140

顯然,UI界面、Worker構(gòu)造函數(shù)、槽函數(shù)處于同一線程(主線程),而run()函數(shù)處于另一線程(次線程)。

2、避免多次connect

當(dāng)多次點(diǎn)擊“開(kāi)始”按鈕的時(shí)候,就會(huì)多次connect(),從而啟動(dòng)多個(gè)線程,同時(shí)更新進(jìn)度條。為了避免這個(gè)問(wèn)題,我們先在mainwindow.h上添加私有成員變量"WorkerThread m_workerThread;",然后修改mainwindow.cpp如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    // 連接信號(hào)槽
    this->connect(ui->pushButton, SIGNAL(clicked(bool)), this, SLOT(startThread()));
 
    this->connect(&m_workerThread, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::handleResults(int value)
{
    qDebug() << "Handle Thread : " << QThread::currentThreadId();
    ui->progressBar->setValue(value);
}
 
void MainWindow::startThread()
{
    if (!m_workerThread.isRunning())
        m_workerThread.start();
}

不再在startThread()函數(shù)內(nèi)創(chuàng)建WorkerThread對(duì)象指針,而是定義私有成員變量,再將connect添加在構(gòu)造函數(shù)中,保證了信號(hào)槽的正常連接。在線程start()之前,可以使用isFinished()和isRunning()來(lái)查詢線程的狀態(tài),判斷線程是否正在運(yùn)行,以確保線程的正常啟動(dòng)。

3、優(yōu)雅地結(jié)束線程的兩種方法

如果一個(gè)線程運(yùn)行完成,就會(huì)結(jié)束。可很多情況并非這么簡(jiǎn)單,由于某種特殊原因,當(dāng)線程還未執(zhí)行完時(shí),我們就想中止它。

不恰當(dāng)?shù)闹兄雇鶗?huì)引起一些未知錯(cuò)誤。比如:當(dāng)關(guān)閉主界面的時(shí)候,很有可能次線程正在運(yùn)行,這時(shí),就會(huì)出現(xiàn)如下提示:

QThread: Destroyed while thread is still running

這是因?yàn)榇尉€程還在運(yùn)行,就結(jié)束了UI主線程,導(dǎo)致事件循環(huán)結(jié)束。這個(gè)問(wèn)題在使用線程的過(guò)程中經(jīng)常遇到,尤其是耗時(shí)操作。大多數(shù)情況下,當(dāng)程序退出時(shí),次線程也許會(huì)正常退出。這時(shí),雖然抱著僥幸心理,但隱患依然存在,也許在極少數(shù)情況下,就會(huì)出現(xiàn)Crash。

所以,我們應(yīng)該采取合理的措施來(lái)優(yōu)雅地結(jié)束線程,一般思路:

  • 發(fā)起線程退出操作,調(diào)用quit()或exit()。
  • 等待線程完全停止,刪除創(chuàng)建在堆上的對(duì)象。
  • 適當(dāng)?shù)氖褂脀ait()(用于等待線程的退出)和合理的算法。

方法一

這種方式是Qt4.x中比較常用的,主要是利用“QMutex互斥鎖 + bool成員變量”的方式來(lái)保證共享數(shù)據(jù)的安全性。在workerthread.h上繼續(xù)添加互斥鎖、析構(gòu)函數(shù)和stop()函數(shù),修改如下:

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
 
#include <QThread>
#include <QMutexLocker>
#include <QDebug>
 
class WorkerThread : public QThread
{
    Q_OBJECT
 
public:
    explicit WorkerThread(QObject *parent = 0)
        : QThread(parent),
          m_bStopped(false)
    {
        qDebug() << "Worker Thread : " << QThread::currentThreadId();
    }
 
    ~WorkerThread()
    {
        stop();
        quit();
        wait();
    }
 
    void stop()
    {
        qDebug() << "Worker Stop Thread : " << QThread::currentThreadId();
        QMutexLocker locker(&m_mutex);
        m_bStopped = true;
    }
 
protected:
    virtual void run() Q_DECL_OVERRIDE 
    {
        qDebug() << "Worker Run Thread : " << QThread::currentThreadId();
        int nValue = 0;
        while (nValue < 100)
        {
            // 休眠50毫秒
            msleep(50);
            ++nValue;
 
            // 準(zhǔn)備更新
            emit resultReady(nValue);
 
            // 檢測(cè)是否停止
            {
                QMutexLocker locker(&m_mutex);
                if (m_bStopped)
                    break;
            }
            // locker超出范圍并釋放互斥鎖
        }
    }
    
signals:
    void resultReady(int value);
 
private:
    bool m_bStopped;
    QMutex m_mutex;
};
 
#endif // WORKERTHREAD_H

當(dāng)主窗口被關(guān)閉,其“子對(duì)象”WorkerThread也會(huì)析構(gòu)調(diào)用stop()函數(shù),使m_bStopped變?yōu)閠rue,則break跳出循環(huán)結(jié)束run()函數(shù),結(jié)束進(jìn)程。當(dāng)主線程調(diào)用stop()更新m_bStopped的時(shí)候,run()函數(shù)也極有可能正在訪問(wèn)它(這時(shí),他們處于不同的線程),所以存在資源競(jìng)爭(zhēng),因此需要加鎖,保證共享數(shù)據(jù)的安全性。

為什么要加鎖?

很簡(jiǎn)單,是為了共享數(shù)據(jù)段操作的互斥。避免形成資源競(jìng)爭(zhēng)的情況(多個(gè)線程有可能訪問(wèn)同一共享資源的情況)。

方法二

Qt5以后,可以使用requestInterruption()、isInterruptionRequested()這兩個(gè)函數(shù),使用很方便,修改workerthread.h文件如下:

#ifndef WORKERTHREAD_H
#define WORKERTHREAD_H
 
#include <QThread>
#include <QMutexLocker>
#include <QDebug>
 
class WorkerThread : public QThread
{
    Q_OBJECT
 
public:
    explicit WorkerThread(QObject *parent = nullptr)
        : QThread(parent)
    {
        qDebug() << "Worker Thread : " << QThread::currentThreadId();
    }
 
    ~WorkerThread()
    {
        // 請(qǐng)求終止
        requestInterruption();
        quit();
        wait();
    }
 
protected:
    virtual void run() Q_DECL_OVERRIDE
    {
        qDebug() << "Worker Run Thread : " << QThread::currentThreadId();
        int nValue = 0;
 
        // 是否請(qǐng)求終止
        while (!isInterruptionRequested())
        {
            while (nValue < 100)
            {
                // 休眠50毫秒
                msleep(50);
                ++nValue;
 
                // 準(zhǔn)備更新
                emit resultReady(nValue);
            }
        }
 
    }
signals:
    void resultReady(int value);
};
 
#endif // WORKERTHREAD_H

在耗時(shí)操作中使用isInterruptionRequested()來(lái)判斷是否請(qǐng)求終止線程,如果沒(méi)有,則一直運(yùn)行;當(dāng)希望終止線程的時(shí)候,調(diào)用requestInterruption()即可。這兩個(gè)函數(shù)內(nèi)部也使用了互斥鎖QMutex。

以上就是一文詳解Qt中線程的實(shí)際應(yīng)用的詳細(xì)內(nèi)容,更多關(guān)于Qt線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語(yǔ)言函數(shù)的基本使用和遞歸詳解

    C語(yǔ)言函數(shù)的基本使用和遞歸詳解

    一個(gè)函數(shù)在它的函數(shù)體內(nèi)調(diào)用它自身稱為遞歸調(diào)用。這種函數(shù)稱為遞歸函數(shù)。C語(yǔ)言允許函數(shù)的遞歸調(diào)用。在遞歸調(diào)用中,主調(diào)函數(shù)又是被調(diào)函數(shù)。執(zhí)行遞歸函數(shù)將反復(fù)調(diào)用其自身,每調(diào)用一次就進(jìn)入新的一層
    2021-09-09
  • C/C++實(shí)現(xiàn)日期計(jì)算器的示例代碼

    C/C++實(shí)現(xiàn)日期計(jì)算器的示例代碼

    本篇文章主要介紹了C/C++實(shí)現(xiàn)日期計(jì)算器的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • C/C++?引用作為函數(shù)的返回值方式

    C/C++?引用作為函數(shù)的返回值方式

    這篇文章主要介紹了C/C++?引用作為函數(shù)的返回值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 帶你了解C++的IO流

    帶你了解C++的IO流

    這篇文章主要介紹了C++ IO流的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c++,感興趣的朋友可以了解下,希望能夠給你帶來(lái)幫助
    2021-09-09
  • 基于Windows API實(shí)現(xiàn)遍歷所有文件并刪除的方法

    基于Windows API實(shí)現(xiàn)遍歷所有文件并刪除的方法

    這篇文章主要介紹了基于Windows API實(shí)現(xiàn)遍歷所有文件并刪除的方法,是win32應(yīng)用程序的一個(gè)比較典型的文件操作應(yīng)用技巧,需要的朋友可以參考下
    2015-04-04
  • C++常量指針,指針常量,指向常量的常指針詳解

    C++常量指針,指針常量,指向常量的常指針詳解

    剛接觸到指針時(shí),關(guān)于C++常量指針,指針常量,指向常量的常指針容易混淆,所以整理下,希望能夠幫助自己也幫助到大家
    2021-10-10
  • vc++實(shí)現(xiàn)的tcp socket客戶端和服務(wù)端示例

    vc++實(shí)現(xiàn)的tcp socket客戶端和服務(wù)端示例

    這篇文章主要介紹了vc++實(shí)現(xiàn)的tcp socket客戶端和服務(wù)端示例,需要的朋友可以參考下
    2014-03-03
  • C++讀取文本文件中的漢字亂碼情況原因及解決

    C++讀取文本文件中的漢字亂碼情況原因及解決

    本文介紹簡(jiǎn)體中文Windows操作系統(tǒng)中,C++讀取文本文件中的漢字亂碼情況原因及解決,文中通過(guò)代碼和圖文給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-01-01
  • c語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)文件 r/w 操作方法

    c語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)文件 r/w 操作方法

    由于在 C 語(yǔ)言中 '\' 一般是轉(zhuǎn)義字符的起始標(biāo)志,故在路徑中需要用兩個(gè) '\' 表示路徑中目錄層次的間隔,也可以使用 '/' 作為路徑中的分隔符,本文重點(diǎn)給大家介紹用c語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)文件 r/w 操作方法,感興趣的朋友一起學(xué)習(xí)吧
    2021-05-05
  • C++示例分析內(nèi)聯(lián)函數(shù)與引用變量及函數(shù)重載的使用

    C++示例分析內(nèi)聯(lián)函數(shù)與引用變量及函數(shù)重載的使用

    為了消除函數(shù)調(diào)用的時(shí)空開(kāi)銷,C++ 提供一種提高效率的方法,即在編譯時(shí)將函數(shù)調(diào)用處用函數(shù)體替換,類似于C語(yǔ)言中的宏展開(kāi)。這種在函數(shù)調(diào)用處直接嵌入函數(shù)體的函數(shù)稱為內(nèi)聯(lián)函數(shù)(Inline Function),又稱內(nèi)嵌函數(shù)或者內(nèi)置函數(shù)
    2022-08-08

最新評(píng)論