Linux下應用程式開發:QT中的多執行緒程式設計(轉)
Linux下應用程式開發:QT中的多執行緒程式設計(轉)[@more@] Qt 作為一種基於 C++ 的跨平臺 GUI 系統,能夠提供給使用者構造圖形使用者介面的強大功能。為了滿足使用者構造複雜圖形介面系統的需求,Qt 提供了豐富的多執行緒程式設計支援。
Qt 作為一種基於 C++ 的跨平臺 GUI 系統,能夠提供給使用者構造圖形使用者介面的強大功能。為了滿足使用者構造複雜圖形介面系統的需求,Qt 提供了豐富的多執行緒程式設計支援。從 2.2 版本開始,Qt 主要從下面三個方面對多執行緒程式設計提供支援:一、構造了一些基本的與平臺無關的執行緒類;二、提交使用者自定義事件的 Thread-safe 方式;三、多種執行緒間同步機制,如訊號量,全域性鎖。這些都給使用者提供了極大的方便。不過,在某些情況下,使用定時器機制能夠比利用 Qt 本身的多執行緒機制更方便地實現所需要的功能,同時也避免了不安全的現象發生。本文不僅對 Qt 中的多執行緒支援機制進行了討論,還著重探討了利用定時器機制模擬多執行緒程式設計的方法。
1、系統對多執行緒程式設計的支援
不同的平臺對 Qt 的多執行緒支援方式是不同的。當使用者在 Windows 作業系統上安裝 Qt 系統時,執行緒支援是編譯器的一個選項,在 Qt 的 mkfiles 子目錄中包括了不同種類編譯器的編譯檔案,其中帶有 -mt 字尾的檔案才是支援多執行緒的。
而在 Unix 作業系統中,執行緒的支援是透過在執行 configure 指令碼檔案時新增 -thread 選項加入的。安裝過程將建立一個獨立的庫,即 libqt-mt,因此要支援多執行緒程式設計時,必須與該庫連結(連結選項為-lqt-mt),而不是與通常的 Qt 庫(-lqt)連結。
另外,無論是何種平臺,在增加執行緒支援時都需要定義宏 QT_THREAD_SUPPORT(即增加編譯選項-DQT_THREAD_SUPPORT)。在 Windows 作業系統中,這一點通常是在 qconfig.h 檔案中增加一個選項來實現的。而在 Unix 系統中通常新增在有關的 Makefile 檔案中。
2、Qt中的執行緒類
在 Qt 系統中與執行緒相關的最重要的類當然是 QThread 類,該類提供了建立一個新執行緒以及控制執行緒執行的各種方法。執行緒是透過 QThread::run() 過載函式開始執行的,這一點很象 Java 語言中的執行緒類。在 Qt 系統中,始終執行著一個GUI 主事件執行緒,這個主執行緒從視窗系統中獲取事件,並將它們分發到各個元件去處理。在 QThread 類中還有一種從非主事件執行緒中將事件提交給一個物件的方法,也就是 QThread::postEvent()方法,該方法提供了 Qt 中的一種 Thread-safe 的事件提交過程。提交的事件被放進一個佇列中,然後 GUI 主事件執行緒被喚醒並將此事件發給相應的物件,這個過程與一般的視窗系統事件處理過程是一樣的。值得注意的是,當事件處理過程被呼叫時,是在主事件執行緒中被呼叫的,而不是在呼叫QThread::postEvent 方法的執行緒中被呼叫。比如使用者可以從一個執行緒中迫使另一個執行緒重畫指定區域:
QWidget *mywidget;
QThread::postEvent(mywidget, new QPaintEvent(QRect(0,0,100,100)));
然而,只有一個執行緒類是不夠的,為編寫出支援多執行緒的程式,還需要實現兩個不同的執行緒對共有資料的互斥訪問,因此 Qt 還提供了 QMutex 類,一個執行緒在訪問臨界資料時,需要加鎖,此時其他執行緒是無法對該臨界資料同時加鎖的,直到前一個執行緒釋放該臨界資料。透過這種方式才能實現對臨界資料的原子操作。
除此之外,還需要一些機制使得處於等待狀態的執行緒在特定情況下被喚醒。QWaitCondition 類就提供了這種功能。當發生特定事件時,QWaitCondition 將喚醒等待該事件的所有執行緒或者喚醒任意一個被選中的執行緒。
3、使用者自定義事件在多執行緒程式設計中的應用
在 Qt 系統中,定義了很多種類的事件,如定時器事件、滑鼠移動事件、鍵盤事件、視窗控制元件事件等。通常,事件都來自底層的視窗系統,Qt 的主事件迴圈函式從系統的事件佇列中獲取這些事件,並將它們轉換為 QEvent,然後傳給相應的 QObjects 物件。
除此之外,為了滿足使用者的需求,Qt 系統還提供了一個 QCustomEvent 類,用於使用者自定義事件,這些自定義事件可以利用 QThread::postEvent() 或者QApplication::postEvent() 被髮給各種控制元件或其他 QObject 例項,而 QWidget 類的子類可以透過 QWidget::customEvent() 事件處理函式方便地接收到這些自定義的事件。需要注意的是:QCustomEvent 物件在建立時都帶有一個型別標識 id 以定義事件型別,為了避免與 Qt 系統定義的事件型別衝突,該 id 值應該大於列舉型別 QEvent::Type 中給出的 "User" 值。
在下面的例子中,顯示了多執行緒程式設計中如何利用使用者自定義事件類。
UserEvent類是使用者自定義的事件類,其事件標識為346798,顯然不會與系統定義的事件型別衝突。
class UserEvent : public QCustomEvent //使用者自定義的事件類
{
public:
UserEvent(QString s) : QCustomEvent(346798), sz(s) { ; }
QString str() const { return sz; }
private:
QString sz;
};
UserThread類是由QThread類繼承而來的子類,在該類中除了定義有關的變數和執行緒控制函式外,最主要的是定義執行緒的啟動函式UserThread::run(),在該函式中建立了一個使用者自定義事件UserEvent,並利用QThread類的postEvent函式提交該事件給相應的接收物件。
class UserThread : public QThread //使用者定義的執行緒類
{
public:
UserThread(QObject *r, QMutex *m, QWaitCondition *c);
QObject *receiver;
}
void UserThread::run() //執行緒類啟動函式,在該函式中建立了一個使用者自定義事件
{UserEvent *re = new UserEvent(resultstring);
QThread::postEvent(receiver, re);
}
UserWidget類是使用者定義的用於接收自定義事件的QWidget類的子類,該類利用slotGo()函式建立了一個新的執行緒recv(UserThread類),當收到相應的自定義事件(即id為346798)時,利用customEvent函式對事件進行處理。
void UserWidget::slotGo() //使用者定義控制元件的成員函式
{ mutex.lock();
if (! recv)
recv = new UserThread(this, &mutex, &condition);
recv->start();
mutex.unlock();
}
void UserWidget::customEvent(QCustomEvent *e) //使用者自定義事件處理函式
{ if (e->type()==346798)
{
UserEvent *re = (UserEvent *) e;
newstring = re->str();
}
}
在這個例子中,UserWidget物件中建立了新的執行緒UserThread,使用者可以利用這個執行緒實現一些週期性的處理(如接收底層發來的訊息等),一旦滿足特定條件就提交一個使用者自定義的事件,當UserWidget物件收到該事件時,可以按需求做出相應的處理,而一般情況下,UserWidget物件可以正常地執行某些例行處理,而完全不受底層訊息的影響。
4、利用定時器機制實現多執行緒程式設計
為了避免Qt系統中多執行緒程式設計帶來的問題,還可以使用系統中提供的定時器機制來實現類似的功能。定時器機制將併發的事件序列化,簡化了對併發事件的處理,從而避免了thread-safe方面問題的出現。
在下面的例子中,同時有若干個物件需要接收底層發來的訊息(可以透過Socket、FIFO等程式間通訊機制),而訊息是隨機收到的,需要有一個GUI主執行緒專門負責接收訊息。當收到訊息時主執行緒初始化相應物件使之開始處理,同時返回,這樣主執行緒就可以始終更新介面顯示並接收外界發來的訊息,達到同時對多個物件的控制;另一方面,各個物件在處理完訊息後需要通知GUI主執行緒。對於這個問題,可以利用第3節中的使用者自定義事件的方法,在主執行緒中安裝一個事件過濾器,來捕捉從各個物件中發來的自定義事件,然後發出訊號呼叫主執行緒中的一個槽函式。
另外,也可以利用Qt中的定時器機制實現類似的功能,而又不必擔心Thread-safe問題。下面就是有關的程式碼部分:
在使用者定義的Server類中建立和啟動了定時器,並利用connect函式將定時器超時與讀取裝置檔案資料相關聯:
Server:: Server(QWidget *parent) : QWidget(parent)
{
readTimer = new QTimer(this); //建立並啟動定時器
connect(readTimer, SIGNAL(timeout()), this, SLOT(slotReadFile())); //每當定時器超時時呼叫函式slotReadFile讀取檔案
readTimer->start(100);
}
slotReadFile函式負責在定時器超時時,從檔案中讀取資料,然後重新啟動定時器:
int Server::slotReadFile() // 訊息讀取和處理函式
{
readTimer->stop(); //暫時停止定時器計時
ret = read(file, buf ); //讀取檔案
if(ret == NULL)
{ readTimer->start(100); //當沒有新訊息時,重新啟動定時器
return(-1);
}
else
根據buf中的內容將訊息分發給各個相應的物件處理……;
readTimer->start(100); //重新啟動定時器
}
在該程式中,利用了類似輪循的方式定時對使用者指定的裝置檔案進行讀取,根據讀到的資料內容將資訊傳送到各個相應的物件。使用者可以在自己的GUI主執行緒中建立一個Server類,幫助實現底層的訊息接收過程,而本身仍然可以處理諸如介面顯示的問題。當各個物件完成處理後,透過重新啟動定時器繼續進行週期性讀取底層裝置檔案的過程。當然,這種方法適合於各物件對事件的處理時間較短,而底層裝置發來訊息的頻率又相對較慢的情況。
Qt 作為一種基於 C++ 的跨平臺 GUI 系統,能夠提供給使用者構造圖形使用者介面的強大功能。為了滿足使用者構造複雜圖形介面系統的需求,Qt 提供了豐富的多執行緒程式設計支援。從 2.2 版本開始,Qt 主要從下面三個方面對多執行緒程式設計提供支援:一、構造了一些基本的與平臺無關的執行緒類;二、提交使用者自定義事件的 Thread-safe 方式;三、多種執行緒間同步機制,如訊號量,全域性鎖。這些都給使用者提供了極大的方便。不過,在某些情況下,使用定時器機制能夠比利用 Qt 本身的多執行緒機制更方便地實現所需要的功能,同時也避免了不安全的現象發生。本文不僅對 Qt 中的多執行緒支援機制進行了討論,還著重探討了利用定時器機制模擬多執行緒程式設計的方法。
1、系統對多執行緒程式設計的支援
不同的平臺對 Qt 的多執行緒支援方式是不同的。當使用者在 Windows 作業系統上安裝 Qt 系統時,執行緒支援是編譯器的一個選項,在 Qt 的 mkfiles 子目錄中包括了不同種類編譯器的編譯檔案,其中帶有 -mt 字尾的檔案才是支援多執行緒的。
而在 Unix 作業系統中,執行緒的支援是透過在執行 configure 指令碼檔案時新增 -thread 選項加入的。安裝過程將建立一個獨立的庫,即 libqt-mt,因此要支援多執行緒程式設計時,必須與該庫連結(連結選項為-lqt-mt),而不是與通常的 Qt 庫(-lqt)連結。
另外,無論是何種平臺,在增加執行緒支援時都需要定義宏 QT_THREAD_SUPPORT(即增加編譯選項-DQT_THREAD_SUPPORT)。在 Windows 作業系統中,這一點通常是在 qconfig.h 檔案中增加一個選項來實現的。而在 Unix 系統中通常新增在有關的 Makefile 檔案中。
2、Qt中的執行緒類
在 Qt 系統中與執行緒相關的最重要的類當然是 QThread 類,該類提供了建立一個新執行緒以及控制執行緒執行的各種方法。執行緒是透過 QThread::run() 過載函式開始執行的,這一點很象 Java 語言中的執行緒類。在 Qt 系統中,始終執行著一個GUI 主事件執行緒,這個主執行緒從視窗系統中獲取事件,並將它們分發到各個元件去處理。在 QThread 類中還有一種從非主事件執行緒中將事件提交給一個物件的方法,也就是 QThread::postEvent()方法,該方法提供了 Qt 中的一種 Thread-safe 的事件提交過程。提交的事件被放進一個佇列中,然後 GUI 主事件執行緒被喚醒並將此事件發給相應的物件,這個過程與一般的視窗系統事件處理過程是一樣的。值得注意的是,當事件處理過程被呼叫時,是在主事件執行緒中被呼叫的,而不是在呼叫QThread::postEvent 方法的執行緒中被呼叫。比如使用者可以從一個執行緒中迫使另一個執行緒重畫指定區域:
QWidget *mywidget;
QThread::postEvent(mywidget, new QPaintEvent(QRect(0,0,100,100)));
然而,只有一個執行緒類是不夠的,為編寫出支援多執行緒的程式,還需要實現兩個不同的執行緒對共有資料的互斥訪問,因此 Qt 還提供了 QMutex 類,一個執行緒在訪問臨界資料時,需要加鎖,此時其他執行緒是無法對該臨界資料同時加鎖的,直到前一個執行緒釋放該臨界資料。透過這種方式才能實現對臨界資料的原子操作。
除此之外,還需要一些機制使得處於等待狀態的執行緒在特定情況下被喚醒。QWaitCondition 類就提供了這種功能。當發生特定事件時,QWaitCondition 將喚醒等待該事件的所有執行緒或者喚醒任意一個被選中的執行緒。
3、使用者自定義事件在多執行緒程式設計中的應用
在 Qt 系統中,定義了很多種類的事件,如定時器事件、滑鼠移動事件、鍵盤事件、視窗控制元件事件等。通常,事件都來自底層的視窗系統,Qt 的主事件迴圈函式從系統的事件佇列中獲取這些事件,並將它們轉換為 QEvent,然後傳給相應的 QObjects 物件。
除此之外,為了滿足使用者的需求,Qt 系統還提供了一個 QCustomEvent 類,用於使用者自定義事件,這些自定義事件可以利用 QThread::postEvent() 或者QApplication::postEvent() 被髮給各種控制元件或其他 QObject 例項,而 QWidget 類的子類可以透過 QWidget::customEvent() 事件處理函式方便地接收到這些自定義的事件。需要注意的是:QCustomEvent 物件在建立時都帶有一個型別標識 id 以定義事件型別,為了避免與 Qt 系統定義的事件型別衝突,該 id 值應該大於列舉型別 QEvent::Type 中給出的 "User" 值。
在下面的例子中,顯示了多執行緒程式設計中如何利用使用者自定義事件類。
UserEvent類是使用者自定義的事件類,其事件標識為346798,顯然不會與系統定義的事件型別衝突。
class UserEvent : public QCustomEvent //使用者自定義的事件類
{
public:
UserEvent(QString s) : QCustomEvent(346798), sz(s) { ; }
QString str() const { return sz; }
private:
QString sz;
};
UserThread類是由QThread類繼承而來的子類,在該類中除了定義有關的變數和執行緒控制函式外,最主要的是定義執行緒的啟動函式UserThread::run(),在該函式中建立了一個使用者自定義事件UserEvent,並利用QThread類的postEvent函式提交該事件給相應的接收物件。
class UserThread : public QThread //使用者定義的執行緒類
{
public:
UserThread(QObject *r, QMutex *m, QWaitCondition *c);
QObject *receiver;
}
void UserThread::run() //執行緒類啟動函式,在該函式中建立了一個使用者自定義事件
{UserEvent *re = new UserEvent(resultstring);
QThread::postEvent(receiver, re);
}
UserWidget類是使用者定義的用於接收自定義事件的QWidget類的子類,該類利用slotGo()函式建立了一個新的執行緒recv(UserThread類),當收到相應的自定義事件(即id為346798)時,利用customEvent函式對事件進行處理。
void UserWidget::slotGo() //使用者定義控制元件的成員函式
{ mutex.lock();
if (! recv)
recv = new UserThread(this, &mutex, &condition);
recv->start();
mutex.unlock();
}
void UserWidget::customEvent(QCustomEvent *e) //使用者自定義事件處理函式
{ if (e->type()==346798)
{
UserEvent *re = (UserEvent *) e;
newstring = re->str();
}
}
在這個例子中,UserWidget物件中建立了新的執行緒UserThread,使用者可以利用這個執行緒實現一些週期性的處理(如接收底層發來的訊息等),一旦滿足特定條件就提交一個使用者自定義的事件,當UserWidget物件收到該事件時,可以按需求做出相應的處理,而一般情況下,UserWidget物件可以正常地執行某些例行處理,而完全不受底層訊息的影響。
4、利用定時器機制實現多執行緒程式設計
為了避免Qt系統中多執行緒程式設計帶來的問題,還可以使用系統中提供的定時器機制來實現類似的功能。定時器機制將併發的事件序列化,簡化了對併發事件的處理,從而避免了thread-safe方面問題的出現。
在下面的例子中,同時有若干個物件需要接收底層發來的訊息(可以透過Socket、FIFO等程式間通訊機制),而訊息是隨機收到的,需要有一個GUI主執行緒專門負責接收訊息。當收到訊息時主執行緒初始化相應物件使之開始處理,同時返回,這樣主執行緒就可以始終更新介面顯示並接收外界發來的訊息,達到同時對多個物件的控制;另一方面,各個物件在處理完訊息後需要通知GUI主執行緒。對於這個問題,可以利用第3節中的使用者自定義事件的方法,在主執行緒中安裝一個事件過濾器,來捕捉從各個物件中發來的自定義事件,然後發出訊號呼叫主執行緒中的一個槽函式。
另外,也可以利用Qt中的定時器機制實現類似的功能,而又不必擔心Thread-safe問題。下面就是有關的程式碼部分:
在使用者定義的Server類中建立和啟動了定時器,並利用connect函式將定時器超時與讀取裝置檔案資料相關聯:
Server:: Server(QWidget *parent) : QWidget(parent)
{
readTimer = new QTimer(this); //建立並啟動定時器
connect(readTimer, SIGNAL(timeout()), this, SLOT(slotReadFile())); //每當定時器超時時呼叫函式slotReadFile讀取檔案
readTimer->start(100);
}
slotReadFile函式負責在定時器超時時,從檔案中讀取資料,然後重新啟動定時器:
int Server::slotReadFile() // 訊息讀取和處理函式
{
readTimer->stop(); //暫時停止定時器計時
ret = read(file, buf ); //讀取檔案
if(ret == NULL)
{ readTimer->start(100); //當沒有新訊息時,重新啟動定時器
return(-1);
}
else
根據buf中的內容將訊息分發給各個相應的物件處理……;
readTimer->start(100); //重新啟動定時器
}
在該程式中,利用了類似輪循的方式定時對使用者指定的裝置檔案進行讀取,根據讀到的資料內容將資訊傳送到各個相應的物件。使用者可以在自己的GUI主執行緒中建立一個Server類,幫助實現底層的訊息接收過程,而本身仍然可以處理諸如介面顯示的問題。當各個物件完成處理後,透過重新啟動定時器繼續進行週期性讀取底層裝置檔案的過程。當然,這種方法適合於各物件對事件的處理時間較短,而底層裝置發來訊息的頻率又相對較慢的情況。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617542/viewspace-949513/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PyQt應用程式中的多執行緒:使用Qt還是Python執行緒?QT執行緒Python
- iOS開發-多執行緒程式設計iOS執行緒程式設計
- Linux C++ 多執行緒程式設計LinuxC++執行緒程式設計
- Linux C/C++程式設計中的多執行緒程式設計基本概念LinuxC++程式設計執行緒
- 併發程式設計-10.使用 Visual Studio 除錯多執行緒應用程式程式設計除錯執行緒
- python 多執行緒程式設計Python執行緒程式設計
- JavaScript多執行緒程式設計JavaScript執行緒程式設計
- Python多執行緒程式設計Python執行緒程式設計
- Linux多執行緒伺服器端程式設計Linux執行緒伺服器程式設計
- 多執行緒併發程式設計“鎖”事執行緒程式設計
- [短文速讀 -5] 多執行緒程式設計引子:程式、執行緒、執行緒安全執行緒程式設計
- 多執行緒程式設計基礎(一)-- 執行緒的使用執行緒程式設計
- 29. 多執行緒程式設計執行緒程式設計
- 多執行緒程式設計ExecutorService用法執行緒程式設計
- 【多執行緒高併發程式設計】二 實現多執行緒的幾種方式執行緒程式設計
- 多執行緒程式設計基礎(二)-- 執行緒池的使用執行緒程式設計
- Android中的多程式、多執行緒Android執行緒
- 多執行緒程式設計的核心思想執行緒程式設計
- java多執行緒程式設計:你真的瞭解執行緒中斷嗎?Java執行緒程式設計
- 基於SkyEye執行Qt:著名應用程式開發框架QT框架
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- 【Linux網路程式設計-5】多執行緒服務端Linux程式設計執行緒服務端
- C++ Qt開發:運用QThread多執行緒元件C++QTthread執行緒元件
- Qt 中多執行緒對應的訊號槽QT執行緒
- 深入理解多執行緒程式設計執行緒程式設計
- Task+ConcurrentQueue多執行緒程式設計執行緒程式設計
- [02] 多執行緒邏輯程式設計執行緒程式設計
- 多執行緒程式設計總結:一、認識多執行緒本質執行緒程式設計
- 併發程式設計之:執行緒程式設計執行緒
- C#多執行緒程式設計-基元執行緒同步構造C#執行緒程式設計
- C#多執行緒程式設計實戰1.1建立執行緒C#執行緒程式設計
- pytest(13)-多執行緒、多程式執行用例執行緒
- 併發程式設計 —— 談談執行緒中斷程式設計執行緒
- 深入淺出Win32多執行緒程式設計--之MFC的多執行緒Win32執行緒程式設計
- Java中的多執行緒程式設計(超詳細總結)Java執行緒程式設計
- 多執行緒程式設計進階——Java類庫中的鎖執行緒程式設計Java
- qt多執行緒QT執行緒
- Java併發程式設計之執行緒篇之執行緒中斷(三)Java程式設計執行緒
- 使用Java實現多執行緒程式設計Java執行緒程式設計