91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Qt中的線程怎么應用

發布時間:2023-03-10 11:43:59 來源:億速云 閱讀:126 作者:iii 欄目:開發技術

今天小編給大家分享一下Qt中的線程怎么應用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

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

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

Qt中的線程怎么應用

首先創建QtGui應用,工程名稱為“myThreadBar”,類名選擇“QMainWindow”,其他選項保持默認即可。再添加一個名稱為WorkerThread的頭文件,定義一個WorkerThread類,讓其繼承自QThread,并重寫run()函數,修改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;
 
            // 準備更新
            emit resultReady(nValue);
        }
    }
 
signals:
    void resultReady(int value);
};
 
#endif // WORKERTHREAD_H

通過在run()函數中調用msleep(50),線程會每隔50毫秒讓當前的進度值加1,然后發射一個resultReady()信號,其余時間什么都不做。在這段空閑時間,線程不占用任何的系統資源。當休眠時間結束,線程就會獲得CPU時鐘,將繼續執行它的指令。

再在mainwindow.ui上添加一個按鈕和進度條部件,然后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:
    // 更新進度
    void handleResults(int value);
 
    // 開啟線程
    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();        
 
    // 連接信號槽
    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)));
    // 線程結束后,自動銷毀
    this->connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater()));
    workerThread->start();
}

由于信號與槽連接類型默認為“Qt::AutoConnection”,在這里相當于“Qt::QueuedConnection”。也就是說,槽函數在接收者的線程(主線程)中執行。

執行程序,“應用程序輸出”窗口輸出如下:

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

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

2、避免多次connect

當多次點擊“開始”按鈕的時候,就會多次connect(),從而啟動多個線程,同時更新進度條。為了避免這個問題,我們先在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);
 
    // 連接信號槽
    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()函數內創建WorkerThread對象指針,而是定義私有成員變量,再將connect添加在構造函數中,保證了信號槽的正常連接。在線程start()之前,可以使用isFinished()和isRunning()來查詢線程的狀態,判斷線程是否正在運行,以確保線程的正常啟動。

3、優雅地結束線程的兩種方法

如果一個線程運行完成,就會結束。可很多情況并非這么簡單,由于某種特殊原因,當線程還未執行完時,我們就想中止它。

不恰當的中止往往會引起一些未知錯誤。比如:當關閉主界面的時候,很有可能次線程正在運行,這時,就會出現如下提示:

QThread: Destroyed while thread is still running

這是因為次線程還在運行,就結束了UI主線程,導致事件循環結束。這個問題在使用線程的過程中經常遇到,尤其是耗時操作。大多數情況下,當程序退出時,次線程也許會正常退出。這時,雖然抱著僥幸心理,但隱患依然存在,也許在極少數情況下,就會出現Crash。

所以,我們應該采取合理的措施來優雅地結束線程,一般思路:

  • 發起線程退出操作,調用quit()或exit()。

  • 等待線程完全停止,刪除創建在堆上的對象。

  • 適當的使用wait()(用于等待線程的退出)和合理的算法。

方法一

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

#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;
 
            // 準備更新
            emit resultReady(nValue);
 
            // 檢測是否停止
            {
                QMutexLocker locker(&m_mutex);
                if (m_bStopped)
                    break;
            }
            // locker超出范圍并釋放互斥鎖
        }
    }
    
signals:
    void resultReady(int value);
 
private:
    bool m_bStopped;
    QMutex m_mutex;
};
 
#endif // WORKERTHREAD_H

當主窗口被關閉,其“子對象”WorkerThread也會析構調用stop()函數,使m_bStopped變為true,則break跳出循環結束run()函數,結束進程。當主線程調用stop()更新m_bStopped的時候,run()函數也極有可能正在訪問它(這時,他們處于不同的線程),所以存在資源競爭,因此需要加鎖,保證共享數據的安全性。

為什么要加鎖?

很簡單,是為了共享數據段操作的互斥。避免形成資源競爭的情況(多個線程有可能訪問同一共享資源的情況)。

方法二

Qt5以后,可以使用requestInterruption()、isInterruptionRequested()這兩個函數,使用很方便,修改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()
    {
        // 請求終止
        requestInterruption();
        quit();
        wait();
    }
 
protected:
    virtual void run() Q_DECL_OVERRIDE
    {
        qDebug() << "Worker Run Thread : " << QThread::currentThreadId();
        int nValue = 0;
 
        // 是否請求終止
        while (!isInterruptionRequested())
        {
            while (nValue < 100)
            {
                // 休眠50毫秒
                msleep(50);
                ++nValue;
 
                // 準備更新
                emit resultReady(nValue);
            }
        }
 
    }
signals:
    void resultReady(int value);
};
 
#endif // WORKERTHREAD_H

在耗時操作中使用isInterruptionRequested()來判斷是否請求終止線程,如果沒有,則一直運行;當希望終止線程的時候,調用requestInterruption()即可。這兩個函數內部也使用了互斥鎖QMutex。

以上就是“Qt中的線程怎么應用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

qt
AI

麻城市| 石棉县| 大宁县| 漯河市| 马鞍山市| 常山县| 萍乡市| 白山市| 棋牌| 桂东县| 泊头市| 昆明市| 合肥市| 合水县| 文成县| 衡山县| 米林县| 永吉县| 望江县| 桑植县| 修武县| 信阳市| 建阳市| 古田县| 闵行区| 蓬莱市| 乌审旗| 阿克| 武邑县| 石河子市| 牙克石市| 额敏县| 五指山市| 河北省| 东兰县| 信阳市| 广灵县| 浠水县| 楚雄市| 闸北区| 大渡口区|