久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

Qt多線程編程總結(jié)(一)

 tianht 2015-04-21

Qt對線程提供了支持,,基本形式有獨(dú)立于平臺的線程類,、線程安全方式的事件傳遞和一個全局Qt庫互斥量允許你可以從不同的線程調(diào)用Qt方法。

這個文檔是提供給那些對多線程編程有豐富的知識和經(jīng)驗(yàn)的聽眾的,。推薦閱讀:

警告:所有的GUI類(比如,,QWidget和它的子類),操作系統(tǒng)核心類(比如,,QProcess)和網(wǎng)絡(luò)類都是線程安全的,。

QRegExp使用一個靜態(tài)緩存并且也不是線程安全的,即使通過使用QMutex來保護(hù)的QRegExp對象,。

線程類

最重要的類是QThread,,也就是說要開始一個新的線程,就是開始執(zhí)行你重新實(shí)現(xiàn)的QThread::run(),。這和Java的線程類很相似。

為了寫線程程序,,在兩個線程同時希望訪問同一個數(shù)據(jù)時,,對數(shù)據(jù)進(jìn)行保護(hù)是很必要的。因此這里也有一個QMutex類,,一個線程可以鎖定互斥量,,并且在它鎖定之后,其它線程就不能再鎖定這個互斥量了,,試圖這樣做的線程都會被阻塞直到互斥量被釋放,。例如:

    class MyClass
    {
    public:
        void doStuff( int );
    	private:
        QMutex mutex;
        int a;
        int b;
    };

    // 這里設(shè)置a為c,b為c*2,。

    void MyClass::doStuff( int c )
    {
        mutex.lock();
	 a = c;
	 b = c * 2;
	mutex.unlock();
    } 

這保證了同一時間只有一個線程可以進(jìn)入MyClass::doStuff(),,所以b將永遠(yuǎn)等于c * 2

另外一個線程也需要在一個給定的條件下等待其它線程的喚醒,,QWaitCondition類就被提供了,。線程等待的條件QWaitCondition指出發(fā)生了什么事情,阻塞將一直持續(xù)到這種事情發(fā)生,。當(dāng)某種事情發(fā)生了,,QWaitCondition可以喚醒等待這一事件的線程之一或全部。(這和POSIX線程條件變量是具有相同功能的并且它也是Unix上的一種實(shí)現(xiàn),。)例如:

    #include <qapplication.h>
    #include <qpushbutton.h>
    // 全局條件變量
    QWaitCondition mycond;
    // Worker類實(shí)現(xiàn)
  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()));
            // 調(diào)用從QThread繼承來的start()方法……這將立即開始線程的執(zhí)行
            QThread::start();
        }
	public slots:
        void slotClicked()
        {    // 喚醒等待這個條件變量的一個線程
            mycond.wakeOne();
        }
	protected:
        void run()
        {
            // 這個方法將被新創(chuàng)建的線程調(diào)用……
            while ( TRUE ) {
                // 鎖定應(yīng)用程序互斥鎖,,并且設(shè)置窗口標(biāo)題來表明我們正在等待開始工作
                qApp->lock();
                setCaption( "Waiting" );
                qApp->unlock();
                // 等待直到我們被告知可以繼續(xù)
                mycond.wait();
                // 如果我們到了這里,我們已經(jīng)被另一個線程喚醒……讓我們來設(shè)置標(biāo)題來表明我們正在工作
                qApp->lock();
                setCaption( "Working!" );
                qApp->unlock();
                // 這可能會占用一些時間,幾秒,、幾分鐘或者幾小時等等,,因?yàn)檫@個一個和GUI線程分開的線程,在處理事件時,,GUI線程不會停下來……
                do_complicated_thing();
            }
        }
    };
	// 主線程——所有的GUI事件都由這個線程處理,。
    int main( int argc, char **argv )
    {
        QApplication app( argc, argv );
        // 創(chuàng)建一個worker……當(dāng)我們這樣做的時候,這個worker將在一個線程中運(yùn)行
        Worker firstworker( 0, "worker" );
        app.setMainWidget( &worker );
        worker.show();
        return app.exec();
    }
  

只要你按下按鈕,,這個程序就會喚醒worker線程,,這個線程將會進(jìn)行并且做一些工作并且然后會回來繼續(xù)等待被告知做更多的工作。如果當(dāng)按鈕被按下時,,worker線程正在工作,,那么就什么也不會發(fā)生。當(dāng)線程完成了工作并且再次調(diào)用QWaitCondition::wait(),,然后它就會被開始,。

線程安全的事件傳遞

在Qt中,一個線程總是一個事件線程——確實(shí)是這樣的,,線程從窗口系統(tǒng)中拉出事件并且把它們分發(fā)給窗口部件,。靜態(tài)方法QThread::postEvent從線程中傳遞事件,而不同于事件線程,。事件線程被喚醒并且事件就像一個普通窗口系統(tǒng)事件那樣在事件線程中被分發(fā),。例如,你可以強(qiáng)制一個窗口部件通過如下這樣做的一個不同的線程來進(jìn)行重繪:

    QWidget *mywidget;
    QThread::postEvent( mywidget, new QPaintEvent( QRect(0, 0, 100, 100) ) );
  

這(異步地)將使mywidget重繪一塊100*100的正方形區(qū)域,。

Qt庫互斥量

Qt庫互斥量提供了從線程而不是事件線程中調(diào)用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中沒有使用互斥量而調(diào)用一個函數(shù)通常情況下結(jié)果將是不可預(yù)知的。從另外一個線程中調(diào)用Qt的一個GUI相關(guān)函數(shù)需要使用Qt庫互斥量,。在這種情況下,,所有可能最終訪問任何圖形或者窗口系統(tǒng)資源的都是GUI相關(guān)的。使用容器類,,字符串或者輸入/輸出類,,如果對象只被一個線程使用就不需要任何互斥量了。

告誡

當(dāng)進(jìn)行線程編程時,,需要注意的一些事情:

  • 當(dāng)使用Qt庫互斥量的時候不要做任何阻塞操作,。這將會凍結(jié)事件循環(huán)。

  • 確認(rèn)你鎖定一個遞歸QMutex的次數(shù)和解鎖的次數(shù)一樣,,不能多也不能少,。

  • 在調(diào)用除了Qt容器和工具類的任何東西之前鎖定Qt應(yīng)用程序互斥量。

  • 謹(jǐn)防隱含地共享類,,你應(yīng)該避免在線程之間使用操作符=()來復(fù)制它們,。這將會在Qt的未來主要的或次要的發(fā)行版本中進(jìn)行改進(jìn),。

  • 謹(jǐn)防那些沒有被設(shè)計為線程安全的Qt類,例如,,QPtrList的應(yīng)用程序接口就不是線程安全的并且如果不同的線程需要遍歷一個QPtrList,,它們應(yīng)該在調(diào)用QPtrList::first()之前鎖定并且在到達(dá)終點(diǎn)之后解鎖,而不是在QPtrList::next()的前后進(jìn)行鎖定和解鎖,。

  • 確認(rèn)只在GUI線程中創(chuàng)建的繼承和使用了QWidget,、QTimerQSocketNotifier的對象。在一些平臺上,,在某個不是GUI線程的線程中創(chuàng)建這樣的對象將永遠(yuǎn)不會接受到底層窗口系統(tǒng)的事件,。

  • 和上面很相似,只在GUI線程中使用QNetwork類,。一個經(jīng)常被問到的問題是一個QSocket是否可以在多線程中使用,。這不是必須得,因?yàn)樗械腝Network類都是異步的,。

  • 不要在不是GUI線程的線程中試圖調(diào)用processEvents()函數(shù),。這也包括QDialog::exec()、QPopupMenu::exec(),、QApplication::processEvents()和其它一些,。

  • 在你的應(yīng)用程序中,不要把普通的Qt庫和支持線程的Qt庫混合使用,。這也就是說如果你的程序使用了支持線程的Qt庫,,你就不應(yīng)該連接普通的Qt庫、動態(tài)的載入普通Qt庫或者動態(tài)地連接其它依賴普通Qt庫的庫或者插件,。在一些系統(tǒng)上,這樣做會導(dǎo)致Qt庫中使用的靜態(tài)數(shù)據(jù)變得不可靠了,。

QT通過三種形式提供了對線程的支持,。它們分別是,一,、平臺無關(guān)的線程類,,二、線程安全的事件投遞,,三,、跨線程的信號-槽連接。這使得開發(fā)輕巧的多線程 Qt程序更為容易,,并能充分利用多處理器機(jī)器的優(yōu)勢,。多線程編程也是一個有用的模式,它用于解決執(zhí)行較長時間的操作而不至于用戶界面失去響應(yīng),。
Qt 線程類
Qt 包含下面一些線程相關(guān)的類:
QThread 提供了開始一個新線程的方法
QThreadStorage 提供逐線程數(shù)據(jù)存儲
QMutex 提供相互排斥的鎖,,或互斥量
QMutexLocker 是一個便利類,,它可以自動對QMutex加鎖與解鎖
QReadWriteLock 提供了一個可以同時讀寫操作的鎖
QReadLocker與QWriteLocker 是便利類,它自動對QReadWriteLock加鎖與解鎖
QSemaphore 提供了一個整型信號量,,是互斥量的泛化
QWaitCondition 提供了一種方法,,使得線程可以在被另外線程喚醒之前一直休眠。

Qt 高級線程類
QtConcurrent 開啟線程事務(wù)
QFutureWatcher 觀測線程狀態(tài)
QFuture 線程啟動類
QThread創(chuàng)建線程
為創(chuàng)建一個線程,,子類化QThread并且重寫它的run()函數(shù),,例如:
class MyThread : public QThread
 {
     Q_OBJECT
 protected:
     void run();
 };
 void MyThread::run()
 {
     ...
 }

之后調(diào)用start,Qt即可創(chuàng)建一個線程,,并在線程中執(zhí)行run()函數(shù)中代碼,,注意UI非線程安全的。

QtConcurrent創(chuàng)建線程
QtConcurrent 創(chuàng)建線程的方法比較多,, 而且QtConcurrent 本身比較特殊,,若系統(tǒng)有空閑線程時,它會調(diào)度空閑線程,,無空閑線程時將會創(chuàng)建一個線程,。
(注意:QtConcurrent 創(chuàng)建線程歸QthreadPool管理,若超過最大線程數(shù),,將會進(jìn)入隊列等待),,QtConcurrent創(chuàng)建線程的方法多種,以下舉例map函數(shù):
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 提供了線程同步的手段,。使用線程的主要想法是希望它們可以盡可能并發(fā)執(zhí)行,,而一些關(guān)鍵點(diǎn)上線程之間需要停止或等待。例如,,假如兩個線程試圖同時訪問同一個 全局變量,,結(jié)果可能不如所愿。

QMutex
QMutex 提供相互排斥的鎖,,或互斥量,。在一個時刻至多一個線程擁有mutex,假如一個線程試圖訪問已經(jīng)被鎖定的mutex,那么它將休眠,直到擁有mutex的線程對此mutex解鎖,。Mutexes常用來保護(hù)共享數(shù)據(jù)訪問,。
QReadWriterLock
QReadWriterLock 與QMutex相似,除了它對 “read”,”write”訪問進(jìn)行區(qū)別對待,。它使得多個讀者可以共時訪問數(shù)據(jù),。使用QReadWriteLock而不是QMutex,可以使得多線程程序更具有并發(fā)性,。
 QReadWriteLock lock;
 void ReaderThread::run()
 {
     lock.lockForRead();
     read_file();
     lock.unlock();
 }
 void WriterThread::run()
 {
     lock.lockForWrite();
     write_file();
     lock.unlock();
 }

QSemaphore
QSemaphore 是QMutex的一般化,,它可以保護(hù)一定數(shù)量的相同資源,與此相對,,一個mutex只保護(hù)一個資源,。下面例子中,,使用QSemaphore來控制對環(huán)狀緩 沖的訪問,此緩沖區(qū)被生產(chǎn)者線程和消費(fèi)者線程共享,。生產(chǎn)者不斷向緩沖寫入數(shù)據(jù)直到緩沖末端,,再從頭開始。消費(fèi)者從緩沖不斷讀取數(shù)據(jù),。信號量比互斥量有更好 的并發(fā)性,,假如我們用互斥量來控制對緩沖的訪問,那么生產(chǎn)者,,消費(fèi)者不能同時訪問緩沖,。然而,我們知道在同一時刻,,不同線程訪問緩沖的不同部分并沒有什么 危害,。
 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 允許線程在某些情況發(fā)生時喚醒另外的線程。一個或多個線程可以阻塞等待一QWaitCondition ,用wakeOne()或wakeAll()設(shè)置一個條件,。wakeOne()隨機(jī)喚醒一個,,wakeAll()喚醒所有。
下面的例子中,,生產(chǎn)者首先必須檢查緩沖是否已滿(numUsedBytes==BufferSize),,如果是,線程停下來等待 bufferNotFull條件,。如果不是,,在緩沖中生產(chǎn)數(shù)據(jù),增加numUsedBytes,激活條件 bufferNotEmpty,。使用mutex來保護(hù)對numUsedBytes的訪問,。另外,QWaitCondition::wait() 接收一個mutex作為參數(shù),,這個mutex應(yīng)該被調(diào)用線程初始化為鎖定狀態(tài),。在線程進(jìn)入休眠狀態(tài)之前,mutex會被解鎖,。而當(dāng)線程被喚醒 時,,mutex會處于鎖定狀態(tài),而且,,從鎖定狀態(tài)到等待狀態(tài)的轉(zhuǎn)換是原子操作,,這阻止了競爭條件的產(chǎn)生。當(dāng)程序開始運(yùn)行時,,只有生產(chǎn)者可以工作,。消費(fèi)者被 阻塞等待bufferNotEmpty條件,一旦生產(chǎn)者在緩沖中放入一個字節(jié),,bufferNotEmpty條件被激發(fā),,消費(fèi)者線程于是被喚醒,。
 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;
 }

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多