Qt多執行緒程式設計總結(一)
Qt對執行緒提供了支援,基本形式有獨立於平臺的執行緒類、執行緒安全方式的事件傳遞和一個全域性Qt庫互斥量允許你可以從不同的執行緒呼叫Qt方法。
這個文件是提供給那些對多執行緒程式設計有豐富的知識和經驗的聽眾的。推薦閱讀:
警告:所有的GUI類(比如,QWidget和它的子類),作業系統核心類(比如,QProcess)和網路類都不是執行緒安全的。
QRegExp使用一個靜態快取並且也不是執行緒安全的,即使通過使用QMutex來保護的QRegExp物件。
執行緒類
最重要的類是QThread,也就是說要開始一個新的執行緒,就是開始執行你重新實現的QThread::run()。這和Java的執行緒類很相似。
為了寫執行緒程式,在兩個執行緒同時希望訪問同一個資料時,對資料進行保護是很必要的。因此這裡也有一個QMutex類,一個執行緒可以鎖定互斥量,並且在它鎖定之後,其它執行緒就不能再鎖定這個互斥量了,試圖這樣做的執行緒都會被阻塞直到互斥量被釋放。例如:
class MyClass { public: void doStuff( int );
private:
QMutex mutex;
int a;
int b; }; // 這裡設定a為c,b為c*2。
void MyClass::doStuff( int c ) {
mutex.lock();
a = c;
b = c * 2;
mutex.unlock(); }
這保證了同一時間只有一個執行緒可以進入MyClass::doStuff(),所以b將永遠等於c * 2。
另外一個執行緒也需要在一個給定的條件下等待其它執行緒的喚醒,QWaitCondition類就被提供了。執行緒等待的條件QWaitCondition指出發生了什麼事情,阻塞將一直持續到這種事情發生。當某種事情發生了,QWaitCondition可以喚醒等待這一事件的執行緒之一或全部。(這和POSIX執行緒條件變數是具有相同功能的並且它也是Unix上的一種實現。)例如:
#include <qapplication.h>
#include <qpushbutton.h>
// 全域性條件變數
QWaitCondition mycond; // Worker類實現
class Worker : public QPushButton, public QThread
{ Q_OBJECT
public:
Worker(QWidget *parent = 0, const char *name = 0)
: QPushButton(parent, name)
{
setText("Start Working");
// 連線從QPushButton繼承來的訊號和我們的slotClicked()方法
connect(this, SIGNAL(clicked()), SLOT(slotClicked()));
// 呼叫從QThread繼承來的start()方法……這將立即開始執行緒的執行
QThread::start();
}
public slots:
void slotClicked()
{ // 喚醒等待這個條件變數的一個執行緒
mycond.wakeOne();
}
protected:
void run()
{
// 這個方法將被新建立的執行緒呼叫……
while ( TRUE ) {
// 鎖定應用程式互斥鎖,並且設定視窗標題來表明我們正在等待開始工作
qApp->lock();
setCaption( "Waiting" );
qApp->unlock();
// 等待直到我們被告知可以繼續
mycond.wait();
// 如果我們到了這裡,我們已經被另一個執行緒喚醒……讓我們來設定標題來表明我們正在工作
qApp->lock();
setCaption( "Working!" );
qApp->unlock();
// 這可能會佔用一些時間,幾秒、幾分鐘或者幾小時等等,因為這個一個和GUI執行緒分開的執行緒,在處理事件時,GUI執行緒不會停下來……
do_complicated_thing();
}
}
};
// 主執行緒——所有的GUI事件都由這個執行緒處理。
int main( int argc, char **argv )
{
QApplication app( argc, argv );
// 建立一個worker……當我們這樣做的時候,這個worker將在一個執行緒中執行
Worker firstworker( 0, "worker" );
app.setMainWidget( &worker );
worker.show();
return app.exec();
}
只要你按下按鈕,這個程式就會喚醒worker執行緒,這個執行緒將會進行並且做一些工作並且然後會回來繼續等待被告知做更多的工作。如果當按鈕被按下時,worker執行緒正在工作,那麼就什麼也不會發生。當執行緒完成了工作並且再次呼叫QWaitCondition::wait(),然後它就會被開始。
執行緒安全的事件傳遞
在Qt中,一個執行緒總是一個事件執行緒——確實是這樣的,執行緒從視窗系統中拉出事件並且把它們分發給視窗部件。靜態方法QThread::postEvent從執行緒中傳遞事件,而不同於事件執行緒。事件執行緒被喚醒並且事件就像一個普通視窗系統事件那樣在事件執行緒中被分發。例如,你可以強制一個視窗部件通過如下這樣做的一個不同的執行緒來進行重繪:
QWidget *mywidget;
QThread::postEvent( mywidget, new QPaintEvent( QRect(0, 0, 100, 100) ) );
這(非同步地)將使mywidget重繪一塊100*100的正方形區域。
Qt庫互斥量
Qt庫互斥量提供了從執行緒而不是事件執行緒中呼叫Qt方法的一種方法。例如:
QApplication *qApp;
QWidget *mywidget;
qApp->lock();
mywidget->setGeometry(0,0,100,100);
QPainter p; p.begin(mywidget); p.drawLine(0,0,100,100); p.end(); qApp->unlock();
在Qt中沒有使用互斥量而呼叫一個函式通常情況下結果將是不可預知的。從另外一個執行緒中呼叫Qt的一個GUI相關函式需要使用Qt庫互斥量。在這種情況下,所有可能最終訪問任何圖形或者視窗系統資源的都是GUI相關的。使用容器類,字串或者輸入/輸出類,如果物件只被一個執行緒使用就不需要任何互斥量了。
告誡
當進行執行緒程式設計時,需要注意的一些事情:
- 當使用Qt庫互斥量的時候不要做任何阻塞操作。這將會凍結事件迴圈。
- 確認你鎖定一個遞迴QMutex的次數和解鎖的次數一樣,不能多也不能少。
- 在呼叫除了Qt容器和工具類的任何東西之前鎖定Qt應用程式互斥量。
- 謹防隱含地共享類,你應該避免線上程之間使用操作符=()來複制它們。這將會在Qt的未來主要的或次要的發行版本中進行改進。
- 謹防那些沒有被設計為執行緒安全的Qt類,例如,QPtrList的應用程式介面就不是執行緒安全的並且如果不同的執行緒需要遍歷一個QPtrList,它們應該在呼叫QPtrList::first()之前鎖定並且在到達終點之後解鎖,而不是在QPtrList::next()的前後進行鎖定和解鎖。
- 確認只在GUI執行緒中建立的繼承和使用了QWidget、QTimer和QSocketNotifier的物件。在一些平臺上,在某個不是GUI執行緒的執行緒中建立這樣的物件將永遠不會接受到底層視窗系統的事件。
- 和上面很相似,只在GUI執行緒中使用QNetwork類。一個經常被問到的問題是一個QSocket是否可以在多執行緒中使用。這不是必須得,因為所有的QNetwork類都是非同步的。
- 不要在不是GUI執行緒的執行緒中試圖呼叫processEvents()函式。這也包括QDialog::exec()、QPopupMenu::exec()、QApplication::processEvents()和其它一些。
- 在你的應用程式中,不要把普通的Qt庫和支援執行緒的Qt庫混合使用。這也就是說如果你的程式使用了支援執行緒的Qt庫,你就不應該連線普通的Qt庫、動態的載入普通Qt庫或者動態地連線其它依賴普通Qt庫的庫或者外掛。在一些系統上,這樣做會導致Qt庫中使用的靜態資料變得不可靠了。
QT通過三種形式提供了對執行緒的支援。它們分別是,一、平臺無關的執行緒類,二、執行緒安全的事件投遞,三、跨執行緒的訊號-槽連線。這使得開發輕巧的多執行緒 Qt程式更為容易,並能充分利用多處理器機器的優勢。多執行緒程式設計也是一個有用的模式,它用於解決執行較長時間的操作而不至於使用者介面失去響應。
Qt 執行緒類
Qt 包含下面一些執行緒相關的類:
QThread 提供了開始一個新執行緒的方法
QThreadStorage 提供逐執行緒資料儲存
QMutex 提供相互排斥的鎖,或互斥量
QMutexLocker 是一個便利類,它可以自動對QMutex加鎖與解鎖
QReadWriteLock 提供了一個可以同時讀寫操作的鎖
QReadLocker與QWriteLocker 是便利類,它自動對QReadWriteLock加鎖與解鎖
QSemaphore 提供了一個整型訊號量,是互斥量的泛化
QWaitCondition 提供了一種方法,使得執行緒可以在被另外執行緒喚醒之前一直休眠。
Qt 高階執行緒類
QtConcurrent 開啟執行緒事務
QFutureWatcher 觀測執行緒狀態
QFuture 執行緒啟動類
QThread建立執行緒
為建立一個執行緒,子類化QThread並且重寫它的run()函式,例如:
class MyThread : public QThread
{
Q_OBJECT
protected:
void run();
};
void MyThread::run()
{
...
}
之後呼叫start,Qt即可建立一個執行緒,並在執行緒中執行run()函式中程式碼,注意UI非執行緒安全的。
QtConcurrent建立執行緒
QtConcurrent 建立執行緒的方法比較多, 而且QtConcurrent 本身比較特殊,若系統有空閒執行緒時,它會排程空閒執行緒,無空閒執行緒時將會建立一個執行緒。
(注意:QtConcurrent 建立執行緒歸QthreadPool管理,若超過最大執行緒數,將會進入佇列等待),QtConcurrent建立執行緒的方法多種,以下舉例map函式:
QImage scale(const QImage &image)
{
qDebug() < < "Scaling image in thread" << QThread::currentThread();
return image.scaled(QSize(100, 100), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
const int imageCount = 20;
// Create a list containing imageCount images.
QList images;
for (int i = 0; i < imageCount; ++i)
images.append(QImage(1600, 1200, QImage::Format_ARGB32_Premultiplied));
// Use QtConcurrentBlocking::mapped to apply the scale function to all the
// images in the list.
QList thumbnails = QtConcurrent::blockingMapped(images, scale);
return 0;
}
Qt 執行緒同步
QMutex, QReadWriteLock, QSemaphore, QWaitCondition 提供了執行緒同步的手段。使用執行緒的主要想法是希望它們可以儘可能併發執行,而一些關鍵點上執行緒之間需要停止或等待。例如,假如兩個執行緒試圖同時訪問同一個 全域性變數,結果可能不如所願。
QMutex
QMutex 提供相互排斥的鎖,或互斥量。在一個時刻至多一個執行緒擁有mutex,假如一個執行緒試圖訪問已經被鎖定的mutex,那麼它將休眠,直到擁有mutex的執行緒對此mutex解鎖。Mutexes常用來保護共享資料訪問。
QReadWriterLock
QReadWriterLock 與QMutex相似,除了它對 “read”,”write”訪問進行區別對待。它使得多個讀者可以共時訪問資料。使用QReadWriteLock而不是QMutex,可以使得多執行緒程式更具有併發性。
QReadWriteLock lock;
void ReaderThread::run()
{
lock.lockForRead();
read_file();
lock.unlock();
}
void WriterThread::run()
{
lock.lockForWrite();
write_file();
lock.unlock();
}
QSemaphore
QSemaphore 是QMutex的一般化,它可以保護一定數量的相同資源,與此相對,一個mutex只保護一個資源。下面例子中,使用QSemaphore來控制對環狀緩 衝的訪問,此緩衝區被生產者執行緒和消費者執行緒共享。生產者不斷向緩衝寫入資料直到緩衝末端,再從頭開始。消費者從緩衝不斷讀取資料。訊號量比互斥量有更好 的併發性,假如我們用互斥量來控制對緩衝的訪問,那麼生產者,消費者不能同時訪問緩衝。然而,我們知道在同一時刻,不同執行緒訪問緩衝的不同部分並沒有什麼
危害。
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QSemaphore freeBytes(BufferSize);
QSemaphore usedBytes;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
freeBytes.acquire();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
usedBytes.release();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
usedBytes.acquire();
fprintf(stderr, "%c", buffer[i % BufferSize]);
freeBytes.release();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
QWaitCondition
QWaitCondition 允許執行緒在某些情況發生時喚醒另外的執行緒。一個或多個執行緒可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()設定一個條件。wakeOne()隨機喚醒一個,wakeAll()喚醒所有。
下面的例子中,生產者首先必須檢查緩衝是否已滿(numUsedBytes==BufferSize),如果是,執行緒停下來等待 bufferNotFull條件。如果不是,在緩衝中生產資料,增加numUsedBytes,啟用條件 bufferNotEmpty。使用mutex來保護對numUsedBytes的訪問。另外,QWaitCondition::wait() 接收一個mutex作為引數,這個mutex應該被呼叫執行緒初始化為鎖定狀態。線上程進入休眠狀態之前,mutex會被解鎖。而當執行緒被喚醒
時,mutex會處於鎖定狀態,而且,從鎖定狀態到等待狀態的轉換是原子操作,這阻止了競爭條件的產生。當程式開始執行時,只有生產者可以工作。消費者被 阻塞等待bufferNotEmpty條件,一旦生產者在緩衝中放入一個位元組,bufferNotEmpty條件被激發,消費者執行緒於是被喚醒。
const int DataSize = 100000;
const int BufferSize = 8192;
char buffer[BufferSize];
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
QMutex mutex;
int numUsedBytes = 0;
class Producer : public QThread
{
public:
void run();
};
void Producer::run()
{
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == BufferSize)
bufferNotFull.wait(&mutex);
mutex.unlock();
buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4];
mutex.lock();
++numUsedBytes;
bufferNotEmpty.wakeAll();
mutex.unlock();
}
}
class Consumer : public QThread
{
public:
void run();
};
void Consumer::run()
{
for (int i = 0; i < DataSize; ++i) {
mutex.lock();
if (numUsedBytes == 0)
bufferNotEmpty.wait(&mutex);
mutex.unlock();
fprintf(stderr, "%c", buffer[i % BufferSize]);
mutex.lock();
--numUsedBytes;
bufferNotFull.wakeAll();
mutex.unlock();
}
fprintf(stderr, "\n");
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
return 0;
}
相關推薦
Qt多執行緒程式設計總結(一)
Qt對執行緒提供了支援,基本形式有獨立於平臺的執行緒類、執行緒安全方式的事件傳遞和一個全域性Qt庫互斥量允許你可以從不同的執行緒呼叫Qt方法。 這個文件是提供給那些對多執行緒程式設計有豐富的知識和經驗的聽眾的。推薦閱讀: 警告:所有的GUI類(比如,QWidget和它的
Qt多執行緒程式設計總結(一)(所有GUI物件都是執行緒不安全的)
Qt對執行緒提供了支援,基本形式有獨立於平臺的執行緒類、執行緒安全方式的事件傳遞和一個全域性Qt庫互斥量允許你可以從不同的執行緒呼叫Qt方法。 這個文件是提供給那些對多執行緒程式設計有豐富的知識和經驗的聽眾的。推薦閱讀: 警告:所有的GUI類(比如,QWidget和它的子類),作業系統核心類(比如,QPr
多執行緒學習總結(一)
一、程序和執行緒的定義 程序:程序是資源(CPU、記憶體等)分配的基本單位,它是程式執行時的一個例項。程式執行時系統就會建立一個程序,併為它分配資源,然後把該程序放入程序就緒佇列,程序排程器選中它的時候就會為它分配CPU時間,程式開始真正執行。 執行緒:執行緒是程式執行時的最小單位,它是程序
多執行緒程式設計入門(一)
多執行緒簡介 1: 什麼是多執行緒 多執行緒,是指從軟體或者硬體上實現多個執行緒併發執行的技術。具有多執行緒能力的計算機因有硬體支援而能夠在同一時間執行多於一個執行緒,進而提升整體處理效能。具有這種能力的系統包括對稱多處理機、多核心處理器以及晶片級多處理
多執行緒程式設計總結(二)——條件變數和互斥鎖
#include <stdio.h> #include <pthread.h> #include <error.h> #include <assert.h> #include <stdlib.h> typedef int DataType; typ
多執行緒詳解(一)
[多執行緒詳解(一)](http://www.neilx.com) 一、概念準備 1、程序 (1)直譯:正在進行中的程式 (2)解釋:執行一個程式時,會在記憶體中為程式開闢空間,這個空間就是一個程序。 (3)注意:一個程序中不可能沒有執行緒,只有有了執行緒才能執行; 程序只
多執行緒學習總結(二)
一、多執行緒帶來的問題 (一)活躍性問題 死鎖:兩個執行緒相互等待對方釋放資源 飢餓: 多執行緒併發時優先順序低的執行緒永遠得不到執行;執行緒被永久阻塞在一個等待進入同步塊的狀態;等待的執行緒永遠不被喚醒 活鎖:活鎖指的是任務或者執行者沒有被阻塞,由於某些條件沒有滿足,導致
java執行緒學習總結(一)
(宣告:並非原創,只是一個簡單總結) 一、執行緒和程序的概念: 程序:程序是處於執行過程中的程式,並且具有一定的對功能,是系統進行資源分配和排程的一個獨立單位。
Java多執行緒-基礎篇(一)
多執行緒---基礎篇,本文主要針對多執行緒的基礎進行復習,內容包括執行緒的理解,建立方式,Thread類函式理解; 1、執行緒的理解:OS系統角度:每個執行的程式都會被作業系統建立對應的程序(包括分配的資源、PCB等),程序是作業系統分配資源的基本單位(理解:程式執行需要空間,作業系統建立對應程序時
Java面向物件與多執行緒綜合實驗(一)之封裝、繼承與多型
編寫一個程式,實現檔案管理系統中的使用者管理模組。要求模組中實現使用者的模擬登入過程。通過使用者輸入,獲取使用者名稱和口令;與事先記錄在程式中的使用者資訊進行對比,通過口令驗證後才能使用系統。使用者分為系統管理人員、檔案錄入人員,檔案瀏覽人員三類,相關類圖如下所示。 (1)要求在使用者類中
多執行緒學習筆記(一)
一. 新建執行緒(兩種方法) 第一種:繼承Thread 1.定義一個類繼承Thread 2.該類重寫run方法。其中的getName是獲取執行緒名方法;有參構造可以在新建時指定執行緒名 3.建立子類物件就是建立新執行緒 4.子類物件呼叫start方法 ,開啟執行
Java多執行緒學習筆記(一)之中斷中的Interrupt,interrupted(),isInterrupted()
1、關於中斷 在Java中中斷最初是通過stop()來終止執行緒的,後來發現這樣簡單粗暴的停止執行緒會產生很多問題(例如物件monitor的釋放),所以改為deprecated,推薦使用interrupt()來中斷執行緒。而對於執行緒來說,會持有一個inter
多執行緒程式設計學習(1)物件及變數的併發訪問
程序:計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。 執行緒:在程序中獨立執行的子任務。 在java中以下3種方法可以終止正在執行的執行緒: 1) 使用退出標誌,使執行緒正常退出,也就是當run方法完成後執行緒終
Java多執行緒與併發(一)
多執行緒與併發的基礎問題 併發就是指程式同時處理多個任務的能力(一個程式被多個使用者訪問都能看到自己預期的結果) 併發的根源在於對多工情況下訪問資源的有效控制! 併發背後的問題 public class DownloadSimple {
Java多執行緒乾貨系列—(一)Java多執行緒基礎
多執行緒併發程式設計是Java程式設計中重要的一塊內容,也是面試重點覆蓋區域,所以學好多執行緒併發程式設計對我們來說極其重要,下面跟我一起開啟本次的學習之旅吧。 正文 執行緒與程序 1 執行緒:程序中負責程式執行的執行單元 執行緒本身依靠程式進行執行 執行緒是程式中的順序控制流,只能使用分配給程式的資源和環
多執行緒基礎知識(一) 執行緒的概念及建立任務與執行緒
執行緒的概念及建立任務與執行緒 引言 多執行緒使得程式中的多個任務可以同時執行。 java的重要功能之一就是內部支援多執行緒————在一個程式中運行同時執行多個任務。在許多程式設計語言中,多執行緒都是通過呼叫依賴於系統的過程或函式來實現的。在本文中,將介紹執行緒的該
Java多執行緒--Monitor物件(一)
1. 什麼是Monitor? Monitor其實是一種同步工具,也可以說是一種同步機制,它通常被描述為一個物件,主要特點是: 物件的所有方法都被“互斥”的執行。好比一個Monitor只有一個執行“許可”,任一個執行緒進入任何一個方法都需要獲得這個“許可”,離開時
【JAVA秒會技術之玩轉多執行緒】多執行緒那些事兒(一)
多執行緒那些事兒(一) 現在只要出去面試,關於“Java多執行緒”的問題,幾乎沒有一家單位不問的,可見其重要性。於是博主抽空研究了一下,確實很有意思!以下是我綜合整理了網上的各種資料,和個人的一些理解,寫的一篇總結博文,僅供學習、交流。 (一)多執行緒的概念
C++11多執行緒程式設計系列(二)實戰
C++11 新標準中引入了多個頭檔案來支援多執行緒程式設計,他們分別是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。 <
對於java多執行緒的理解(一)
解釋:程序的顆粒度太大,每次都要有上下的調入,儲存,調出。如果我們把程序比喻為一個執行在電腦上的軟體,那麼一個軟體的執行不可能是一條邏輯執行的,必定有多個分支和多個程式段,就好比要實現程式A,實際分成 a,b,c等多個塊組合而成。那麼這裡具體的執行就可能變成:程式A得到CPU =》CPU載入上下文,開始執行程