深入理解Qt多线程

浏览:
字体:
发布时间:2013-12-11 11:03:09
来源:

提要

Qt对线程提供了支持,基本形式有独立于平台的线程类、线程安全方式的事件传递和一个全局Qt库互斥量允许你可以从不同的线程调用Qt方法。

每个程序启动后就会拥有一个线程。该线程称为”主线程”(在Qt应用程序中也叫”GUI线程”)。Qt GUI必须运行在此线程上。所有的图形元件和几个相关的类,如QPixmap,不能工作于非主线程中。非主线程通常称为”工作者线程”,因为它主要处理从主线程中卸下的一些工作。

有时候,你需要的不仅仅是在另一线程的上下文中运行一个函数。您可能需要有一个生存在另一个线程中的对象来为 GUI线程提供服务。也许你想在另一个始终运行的线程中来轮询硬件端口并在有关注的事情发生时发送信号到GUI线程。Qt为开发多线程应用程序提供了多种 不同的解决方案。解决方案的选择依赖于新线程的目的以及线程的生命周期。

 

环境

Ubuntu 12.04 64bit

Qt 4.8.1

 

一个简单的例子

首先来看一个单线程的例子。

用Qt Creator创建一个Qt Gui工程,只有一个mainwindow类,代码如下:

mainwindow.h

 

#ifndef MAINWINDOW_H#define MAINWINDOW_H#include #include #include #include class MainWindow : public QMainWindow{    Q_OBJECTprivate:    QPushButton *calButton;    QPushButton *hiButton;    QLabel *mLabel;public:    MainWindow(QWidget *parent = 0);    ~MainWindow();private slots:    void slotGetPi();    void slotSayHi();};#endif // MAINWINDOW_H

mainwindow.cpp

 

 

#include mainwindow.hMainWindow::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~);}

main.cpp

 

 

#include #include mainwindow.hint main(int argc, char *argv[]){    QApplication a(argc, argv);    MainWindow w;    w.show();        return a.exec();}

代码很简单,就是一个窗口中有两个Button和一个label, 一个Button计算Pi,一个Button say hi 两个button都可以更新label.

 

预记的运行效果是点击button之后就可以改变label的值,但实际情况是...

/

 

因为我在点击GetPi这个Button的时候,程序就开始计算,当然是在主线程中,这时候整个界面就阻塞了,Hi Button 设置关闭窗口操作都无法完成,这时就不得不用线程了。

 

用线程改写一下。

创建一个ComputeThread类,继承自QThread。

computethread.h

 

#ifndef COMPUTETHREAD_H#define COMPUTETHREAD_H#include #include #include class ComputeThread : public QThread{    Q_OBJECTprivate:    void run();public:    explicit ComputeThread(QObject *parent = 0);    signals:     void computeFinish(double result);public slots:    };#endif // COMPUTETHREAD_H

computethread.cpp

 

 

#include computethread.hComputeThread::ComputeThread(QObject *parent) :    QThread(parent){}void ComputeThread::run(){    qDebug()<currentThreadId()<<:Begin computing!<computeFinish(result);}

在run中定义线程运行的内容,还定义了一个信号,在计算完毕的时候将结果发射出去。

 

 

mainwindow中添加一个ComputeThread对象和一个槽。

 

private:    ComputeThread *computePiThread;private slots:    void slotShowResult(double result);

cpp中加入线程操作的部分:

 

 

#include mainwindow.hMainWindow::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));}

运行结果

 

/

 

修改之后计算就在子线程中进行,主线程就没有卡死的情况了。

 

MultiThread in Opengl

之前有用QT作为框架来学习OpenGL,参考这里。

当是有个问题没有解决,就是当想要GLWidget中的图形不断的进行变换的话,就要在主线程中加一个死循环,这样做只是权宜之记,最好的解决方法就是用多线程。

创建一个GLThread类专门用来渲染:

glthread.h

 

#ifndef GLTHREAD_H#define GLTHREAD_H#include #include #include #includeclass 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.hGLThread::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也要进行相应的修改:

glwidget.h

 

#ifndef GLWIDGET_H#define GLWIDGET_H#include #include glthread.h#include 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

glwidget.cpp

 

 

#include glwidget.hGLWidget::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);   }

注意这里用到了C++的一个小技巧,前向声明。当两个类要互相引用的时候不能够互相包含头文件,在一个类的头文件中,必须用Class + 类名作为前向声明。而在这个类的cpp中要访问另一个类的具体方法的话,必须包含那个类的头文件。

 

这里还涉及到数据的访问。最开始的例子用的是信号槽的方式进行访问,而这里直接使用的指针进行访问。

 

渲染结果:一个不断旋转的正方形,(假装看见了...)

/

 

线程安全

待研究

一些琐碎

待研究

 

>更多相关文章
24小时热门资讯
24小时回复排行
资讯 | QQ | 安全 | 编程 | 数据库 | 系统 | 网络 | 考试 | 站长 | 关于东联 | 安全雇佣 | 搞笑视频大全 | 微信学院 | 视频课程 |
关于我们 | 联系我们 | 广告服务 | 免责申明 | 作品发布 | 网站地图 | 官方微博 | 技术培训
Copyright © 2007 - 2024 Vm888.Com. All Rights Reserved
粤公网安备 44060402001498号 粤ICP备19097316号 请遵循相关法律法规
');})();