您好,登錄后才能下訂單哦!
Qt對(duì)線程提供了支持,基本形式有獨(dú)立于平臺(tái)的線程類(lèi)、線程安全方式的事件傳遞和一個(gè)全局Qt庫(kù)互斥量允許你可以從不同的線程調(diào)用Qt方法。
每個(gè)程序啟動(dòng)后就會(huì)擁有一個(gè)線程。該線程稱(chēng)為”主線程”(在Qt應(yīng)用程序中也叫”GUI線程”)。Qt GUI必須運(yùn)行在此線程上。所有的圖形元件和幾個(gè)相關(guān)的類(lèi),如QPixmap,不能工作于非主線程中。非主線程通常稱(chēng)為”工作者線程”,因?yàn)樗饕幚韽闹骶€程中卸下的一些工作。
有時(shí)候,你需要的不僅僅是在另一線程的上下文中運(yùn)行一個(gè)函數(shù)。您可能需要有一個(gè)生存在另一個(gè)線程中的對(duì)象來(lái)為 GUI線程提供服務(wù)。也許你想在另一個(gè)始終運(yùn)行的線程中來(lái)輪詢(xún)硬件端口并在有關(guān)注的事情發(fā)生時(shí)發(fā)送信號(hào)到GUI線程。Qt為開(kāi)發(fā)多線程應(yīng)用程序提供了多種 不同的解決方案。解決方案的選擇依賴(lài)于新線程的目的以及線程的生命周期。
環(huán)境
Ubuntu 12.04 64bit
Qt 4.8.1
首先來(lái)看一個(gè)單線程的例子。
用Qt Creator創(chuàng)建一個(gè)Qt Gui工程,只有一個(gè)mainwindow類(lèi),代碼如下:
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QtGui/QMainWindow> #include <QPushButton> #include <QLabel> #include <QHBoxLayout> class MainWindow : public QMainWindow { Q_OBJECT private: QPushButton *calButton; QPushButton *hiButton; QLabel *mLabel; public: MainWindow(QWidget *parent = 0); ~MainWindow(); private slots: void slotGetPi(); void slotSayHi(); }; #endif // MAINWINDOW_H
#include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QHBoxLayout *mainLayout=new QHBoxLayout(); calButton = new QPushButton(this); calButton->setText("GetPi"); hiButton = new QPushButton(this); hiButton->setText("Hi"); mLabel = new QLabel(); mLabel->setText("Bitch"); mainLayout->setSpacing(10); mainLayout->addWidget(calButton); mainLayout->addWidget(hiButton); mainLayout->addWidget(mLabel); QWidget *centreWidget=new QWidget(this); centreWidget->setLayout(mainLayout); this->setCentralWidget(centreWidget); this->connect(calButton,SIGNAL(released()),this, SLOT(slotGetPi())); this->connect(hiButton,SIGNAL(released()),this, SLOT(slotSayHi())); } MainWindow::~MainWindow() { } void MainWindow::slotGetPi() { int time = 1000000000; float result=0; for(int i=1;i<=time;i++) { double value=4.0/(2*i-1); if (i % 2 == 1) result+=value; else result-=value; } mLabel->setText(QString::number(result)); } void MainWindow::slotSayHi() { mLabel->setText("Hei,gay~"); }
#include <QtGui/QApplication> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
預(yù)記的運(yùn)行效果是點(diǎn)擊button之后就可以改變label的值,但實(shí)際情況是...
因?yàn)槲以邳c(diǎn)擊GetPi這個(gè)Button的時(shí)候,程序就開(kāi)始計(jì)算,當(dāng)然是在主線程中,這時(shí)候整個(gè)界面就阻塞了,Hi Button 設(shè)置關(guān)閉窗口操作都無(wú)法完成,這時(shí)就不得不用線程了。
用線程改寫(xiě)一下。
創(chuàng)建一個(gè)ComputeThread類(lèi),繼承自QThread。
computethread.h
#ifndef COMPUTETHREAD_H #define COMPUTETHREAD_H #include <QThread> #include <QDebug> #include <computethread.h> class ComputeThread : public QThread { Q_OBJECT private: void run(); public: explicit ComputeThread(QObject *parent = 0); signals: void computeFinish(double result); public slots: }; #endif // COMPUTETHREAD_H
#include "computethread.h" ComputeThread::ComputeThread(QObject *parent) : QThread(parent) { } void ComputeThread::run() { qDebug()<<this->currentThreadId()<<":Begin computing!"<<endl; int time = 1000000000; float result=0; for(int i=1;i<=time;i++) { double value=4.0/(2*i-1); if (i % 2 == 1) result+=value; else result-=value; } emit this->computeFinish(result); }
mainwindow中添加一個(gè)ComputeThread對(duì)象和一個(gè)槽。
private: ComputeThread *computePiThread; private slots: void slotShowResult(double result);
#include "mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { QVBoxLayout *mainLayout=new QVBoxLayout(); calButton = new QPushButton(this); calButton->setText("GetPi"); hiButton = new QPushButton(this); hiButton->setText("Hi"); mLabel = new QLabel(); mLabel->setText("Bitch"); computePiThread = new ComputeThread; mainLayout->setSpacing(10); mainLayout->addWidget(calButton); mainLayout->addWidget(hiButton); mainLayout->addWidget(mLabel); QWidget *centreWidget=new QWidget(this); centreWidget->setLayout(mainLayout); this->setCentralWidget(centreWidget); this->connect(computePiThread,SIGNAL(computeFinish(double)),this, SLOT(slotShowResult(double))); this->connect(hiButton,SIGNAL(released()),this, SLOT(slotSayHi())); this->connect(calButton,SIGNAL(released()),this, SLOT(slotGetPi())); } MainWindow::~MainWindow() { computePiThread->terminate(); computePiThread->wait(); delete computePiThread; computePiThread = 0; } void MainWindow::slotGetPi() { computePiThread->start(); } void MainWindow::slotSayHi() { mLabel->setText("Hei,gay~"); } void MainWindow::slotShowResult(double result) { mLabel->setText(QString::number(result)); }
修改之后計(jì)算就在子線程中進(jìn)行,主線程就沒(méi)有卡死的情況了。
之前有用QT作為框架來(lái)學(xué)習(xí)OpenGL,參考這里。
當(dāng)是有個(gè)問(wèn)題沒(méi)有解決,就是當(dāng)想要GLWidget中的圖形不斷的進(jìn)行變換的話,就要在主線程中加一個(gè)死循環(huán),這樣做只是權(quán)宜之記,最好的解決方法就是用多線程。
創(chuàng)建一個(gè)GLThread類(lèi)專(zhuān)門(mén)用來(lái)渲染:
glthread.h
#ifndef GLTHREAD_H #define GLTHREAD_H #include <QThread> #include <QSize> #include <QTime> #include<GL/glu.h> class GLWidget; class GLThread : public QThread { public: GLThread(GLWidget *glWidget); void resizeViewport(const QSize &size); void run(); void stop(); private: bool doRendering; bool doResize; int w; int h; GLWidget *glw; }; #endif // GLTHREAD_H
glthread.cpp
#include "glthread.h" #include "glwidget.h" GLThread::GLThread(GLWidget *gl) : QThread(), glw(gl) { doRendering = true; doResize = false; } void GLThread::stop() { doRendering = false; } void GLThread::resizeViewport(const QSize &size) { w = size.width(); h = size.height(); doResize = true; } void GLThread::run() { glw->makeCurrent(); this->rotAngle = 0.0; glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // This Will Clear The Background Color To Black glClearDepth(1.0); // Enables Clearing Of The Depth Buffer glDepthFunc(GL_LESS); // The Type Of Depth Test To Do glEnable(GL_DEPTH_TEST); // Enables Depth Testing glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading glMatrixMode(GL_PROJECTION); glLoadIdentity(); // Reset The Projection Matrix gluPerspective(45.0f,(GLfloat)w/(GLfloat)h,0.1f,100.0f); // Calculate The Aspect Ratio Of The Window glMatrixMode(GL_MODELVIEW); while (doRendering) { rotAngle +=5; if(rotAngle>=360) rotAngle = 0; if (doResize) { glViewport(0, 0, w, h); doResize = false; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)w/(GLfloat)h,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); } glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The View glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0 glRotatef(rotAngle,0.0f,0.0f,1.0f); // Rotate The Triangle On The Y axis // draw a triangle (in smooth coloring mode) glBegin(GL_POLYGON); // start drawing a polygon glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red glVertex3f( 0.0f, 1.0f, 0.0f); // Top glColor3f(0.0f,1.0f,0.0f); // Set The Color To Green glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right glColor3f(0.0f,0.0f,1.0f); // Set The Color To Blue glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left glEnd(); // we're done with the polygon (smooth color interpolation) glw->swapBuffers(); msleep(50); qDebug("rendering"); } }
GLWidget也要進(jìn)行相應(yīng)的修改:
glwidget.h
#ifndef GLWIDGET_H #define GLWIDGET_H #include <QGLWidget> #include "glthread.h" #include <QResizeEvent> class GLWidget : public QGLWidget { public: GLWidget(QWidget *parent); void startRendering(); void stopRendering(); protected: void resizeEvent(QResizeEvent *evt); void paintEvent(QPaintEvent *); void closeEvent(QCloseEvent *evt); GLThread glt; }; #endif // GLWIDGET_H
#include "glwidget.h" GLWidget::GLWidget(QWidget *parent) : glt(this) { setAutoBufferSwap(false); resize(320, 240); } void GLWidget::startRendering() { glt.start(); } void GLWidget::stopRendering() { glt.stop(); glt.wait(); } void GLWidget::resizeEvent(QResizeEvent *evt) { glt.resizeViewport(evt->size()); } void GLWidget::paintEvent(QPaintEvent *) { // Handled by the GLThread. } void GLWidget::closeEvent(QCloseEvent *evt) { stopRendering(); QGLWidget::closeEvent(evt); }
這里還涉及到數(shù)據(jù)的訪問(wèn)。最開(kāi)始的例子用的是信號(hào)槽的方式進(jìn)行訪問(wèn),而這里直接使用的指針進(jìn)行訪問(wèn)。
渲染結(jié)果:一個(gè)不斷旋轉(zhuǎn)的正方形,(假裝看見(jiàn)了...)
QtConcurrent
提供了一些高級(jí)API,使得寫(xiě)多線程程序可以不再使用像互斥、讀寫(xiě)鎖、等待條件、信號(hào)量等低級(jí)的多線程命令。用QtConcurrent寫(xiě)的程序可以根據(jù)內(nèi)核數(shù)量自動(dòng)調(diào)整線程數(shù)。這意味著今天寫(xiě)的應(yīng)用程序?qū)?lái)可以部署在多核系統(tǒng)上。
盡管如此,QtConcurrent 不能用于線程運(yùn)行時(shí)需要通信的情況,而且它也不應(yīng)該被用來(lái)處理阻塞操作。
QReadWriteLock
是一個(gè)讀寫(xiě)鎖,主要用來(lái)同步保護(hù)需要讀寫(xiě)的資源。當(dāng)你想多個(gè)讀線程可以同時(shí)讀取資源,但是只能有一個(gè)寫(xiě)線程操作資源,而其他線程必須等待寫(xiě)線程完成時(shí),這時(shí)候用這個(gè)讀寫(xiě)鎖就很有用了??梢詫?shí)現(xiàn)多個(gè)讀,一個(gè)寫(xiě),讀之間可以不同步不互斥,寫(xiě)時(shí)會(huì)阻塞其他的寫(xiě)操作。QReadWriteLock也有遞歸和非遞歸模式之分。
用法
QReadWriteLock lock; void ReaderThread::run() { lock.lockForRead(); read_file(); lock.unlock(); } void WriterThread::run() { lock.lockForWrite(); write_file(); lock.unlock(); }
解析Qt中QThread使用方法 - http://mobile.51cto.com/symbian-268690_all.htm
Glimpsing the Third Dimension - http://doc.qt.digia.com/qq/qq06-glimpsing.html#writingmultithreadedglapplications
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。