1. 程式人生 > 其它 >Qt多執行緒及執行緒池的使用筆記(一)

Qt多執行緒及執行緒池的使用筆記(一)

Qt多執行緒及執行緒池的使用筆記(一)

​ 在進行桌面應用程式開發的時候,假設應用程式在某些情況下血要處理較為複雜的邏輯,如果只有一個執行緒去處理,就會導致視窗卡頓,無法處理使用者的相關操作,這種情況下就學要使用多執行緒,其中一個執行緒處理視窗事件,其他執行緒處理運算邏輯,多個執行緒各司其職,不僅可以提高使用者體驗還可以提升程式的執行效率。

在Qt中使用了多執行緒,需要注意的一些概念是:

  • 預設的執行緒在Qt中稱之為視窗執行緒,也就叫主執行緒,負責視窗事件處理或者視窗控制元件資料的更新;

  • 子執行緒負責後臺的業務邏輯處理,子執行緒中不能對視窗物件做任何操作,這些事情需要交給視窗執行緒處理;

  • 主執行緒和子執行緒之間如果要進行資料的傳遞,需要使用Qt中的訊號槽機制

  • 子執行緒一般不允許越級進行對視窗引數進行操作

(一)Qt執行緒類QThread簡介

​ Qt提供了一個執行緒類QThread ,通過這個類就需要可以建立子執行緒了,Qt中一共提供了兩種建立子執行緒的方式,後邊會依次介紹其他使用方式,在本例中提供QThread中詳細使用方法基本案例。

1.標頭檔案

#include<QThread>

2.QThread 其常用API介紹

// QThread 類常用 API
// 建構函式
QThread::QThread(QObject *parent = Q_NULLPTR);
// 判斷執行緒中的任務是不是處理完畢了
bool QThread::isFinished() const;
// 判斷子執行緒是不是在執行任務
bool QThread::isRunning() const;

// Qt中的執行緒可以設定優先順序
// 得到當前執行緒的優先順序
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
優先順序:
    QThread::IdlePriority         --> 最低的優先順序
    QThread::LowestPriority
    QThread::LowPriority
    QThread::NormalPriority
    QThread::HighPriority
    QThread::HighestPriority
    QThread::TimeCriticalPriority --> 最高的優先順序
    QThread::InheritPriority      --> 子執行緒和其父執行緒的優先順序相同, 預設是這個
// 退出執行緒, 停止底層的事件迴圈
// 退出執行緒的工作函式
void QThread::exit(int returnCode = 0);
// 呼叫執行緒退出函式之後, 執行緒不會馬上退出因為當前任務有可能還沒有完成, 調回用這個函式是
// 等待任務完成, 然後退出執行緒, 一般情況下會在 exit() 後邊呼叫這個函式
bool QThread::wait(unsigned long time = ULONG_MAX);


3.QThread的訊號與槽函式

// 和呼叫 exit() 效果是一樣的
// 代用這個函式之後, 再呼叫 wait() 函式
[slot] void QThread::quit();
// 啟動子執行緒
[slot] void QThread::start(Priority priority = InheritPriority);
//執行緒退出, 可能是會馬上終止執行緒, 一般情況下不使用這個函式
[slot] void QThread::terminate();

// 執行緒中執行的任務完成了, 發出該訊號
// 任務函式中的處理邏輯執行完畢了
[signal] void QThread::finished();
// 開始工作之前發出這個訊號, 一般不使用
[signal] void QThread::started();

4.QThread靜態函式

// 返回一個指向管理當前執行執行緒的QThread的指標
[static] QThread *QThread::currentThread();
// 返回可以在系統上執行的理想執行緒數 == 和當前電腦的 CPU 核心數相同
[static] int QThread::idealThreadCount();
// 執行緒休眠函式
[static] void QThread::msleep(unsigned long msecs);	// 單位: 毫秒
[static] void QThread::sleep(unsigned long secs);	// 單位: 秒
[static] void QThread::usleep(unsigned long usecs);	// 單位: 微秒

5.任務處理函式

// 子執行緒要處理什麼任務, 需要寫到 run() 中
[virtual protected] void QThread::run();

這個 run() 是一個虛擬函式,如果想讓建立的子執行緒執行某個任務,需要寫一個子類讓其繼承 QThread,並且在子類中重寫父類的 run() 方法,函式體就是對應的任務處理流程。另外,這個函式是一個受保護的成員函式,不能夠在類的外部呼叫,如果想要讓執行緒執行這個函式中的業務流程,需要通過當前執行緒物件呼叫槽函式 start() 啟動子執行緒,當子執行緒被啟動,這個 run() 函式也就線上程內部被呼叫了

(二)兩種基本QThread類使用方法

​ 關於QThread是Qt中用於在多執行緒中執行程式碼的核心類,該類是QObject的一個子類。

​ 關於QThread如何使用,Qt官方提供了兩種方法:

1.方法一

​ 步驟一:子類化QThread(定義一個類繼承QThread)並重新實現run();

​ 步驟二:然後建立子類的例項並呼叫start()成員函式執行執行緒。

​ 具體操作如下程式碼片段:

​ Step One:需要建立一個執行緒類的子類,讓其繼承QT中的執行緒類QThread,比如:

class MyThread:public QThread
{
    ......
}

​ Step Two:重寫父類的run()方法,在該函式內部編寫具體的業務流程:

class MyThread:public QThread
{
    ......
 protected:
    void run()
    {
        ........
    }
}

Step Three:在主執行緒中建立子執行緒物件,new一個就可以了:

MyThread * subThread = new MyThread;

Step Four : 啟動子執行緒,呼叫start()方法:

subThread->start();

注意:當子執行緒別創建出來之後,父子執行緒之間的通訊可以通過訊號槽的方式:

  1. 在 Qt 中在子執行緒中不要操作程式中的視窗型別物件,不允許,如果操作了程式就掛了;
  2. 只有主執行緒才能操作程式中的視窗物件,預設的執行緒就是主執行緒,自己建立的就是子執行緒。

2.方法二

​ Qt 提供的第二種執行緒的建立方式彌補了第一種方式的缺點,用起來更加靈活,但是這種方式寫起來會相對複雜一些,其具體操作步驟如下:

​ Step One:建立一個新的類,讓這個類從QObejct派生:

class MyWork:public QObject
{
    .......
}

​ Step Two:在這個類中新增一個公共的成員函式,函式體就是我們要子執行緒中執行的業務邏輯:

class MyWork:public QObject
{
public:
    .......
    // 函式名自己指定, 叫什麼都可以, 引數可以根據實際需求新增
    void working();
}

Step Three:在主執行緒中建立一個 QThread 物件,這就是子執行緒的物件,new一個就可以了

QThread* sub = new QThread;

Step Four:在主執行緒中建立工作的類物件(千萬不要指定給建立的物件指定父物件)

MyWork* work = new MyWork(this);    // error
MyWork* work = new MyWork;          // ok

Step Five:將 MyWork 物件移動到建立的子執行緒物件中,需要呼叫 QObject 類提供的 moveToThread() 方法

// void QObject::moveToThread(QThread *targetThread);
// 如果給work指定了父物件, 這個函式呼叫就失敗了
// 提示: QObject::moveToThread: Cannot move objects with a parent
work->moveToThread(sub);	// 移動到子執行緒中工作

Step Sixth: 啟動子執行緒,呼叫 start(), 這時候執行緒啟動了,但是移動到執行緒中的物件並沒有工作

Step Seventh: 呼叫 MyWork 類物件的工作函式,讓這個函式開始執行,這時候是在移動到的那個子執行緒中執行的

注意:

   使用這種多執行緒方式,假設有多個不相關的業務流程需要被處理,那麼就可以建立多個類似於 MyWork 的類,將業務流程放多類的公共成員函式中,然後將這個業務類的例項物件移動到對應的子執行緒中 moveToThread() 就可以了,這樣可以讓編寫的程式更加靈活,可讀性更強,更易於維護。