【问题标题】:c++11 thread initialized in constructor to execute methodc++11线程在构造函数中初始化以执行方法
【发布时间】:2014-10-21 04:09:38
【问题描述】:

我想渲染一个三角形,它的坐标由一个单独的线程不断更新(使用 m_offset)。线程在整个运行时并行运行。

我的应用程序使用 GL 小部件初始化 QT 窗口。

#include "mainwindow.h"
#include <QApplication>
#include <iostream>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

GL 小部件(glwidget.cpp)定义如下:

#include "glwidget.h"

#include <iostream>
#include <thread>

GLWidget::GLWidget(QWidget *parent) : QGLWidget(parent)
{

    m_offset = 0.0

    // HERE, I want to start a thread on updateCoordinates().
    // Something like this: (this doesn't work)
    //std::thread mythread (updateCoordinates);   
    // or this (getting segfault)
    //std::thread mythread (&GLWidget::updateCoordinates, this);


}

void GLWidget::initializeGL()
{
    glClearColor(0.2, 0.2, 0.2, 1);
}
void GLWidget::paintGL()
{


    glClear(GL_COLOR_BUFFER_BIT);

    glBegin(GL_TRIANGLES);
        glColor3f(1,0,0);
        glVertex3f(-0.5+m_offset, -0.5+m_offset, 0);
        glColor3f(0,1,0);
        glVertex3f(0.5+m_offset, -0.5+m_offset, 0);
        glColor3f(0,0,1);
        glVertex3f(0.0+m_offset, 0.5+m_offset, 0);
    glEnd();

}
void GLWidget::resizeGL(int w, int h)
{}


void GLWidget::updateCoordinates()
{

    while(true)
    {
        m_offset += 0.0001;
    }

}

这里是对应的标题:

#ifndef GLWIDGET_H
#define GLWIDGET_H

#include <QGLWidget>

class GLWidget : public QGLWidget
{
    Q_OBJECT
public:
    explicit GLWidget(QWidget *parent = 0);

    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);

    void startSimulation();
    void stopSimulation();
    void updateCoordinates();

private:
    double m_offset;

signals:

public slots:

};

#endif // GLWIDGET_H

如何在此类中生成一个持续运行updateCoordinates() 的新线程,同时通过paingGL() 更新图形?

谢谢!

【问题讨论】:

    标签: multithreading qt c++11


    【解决方案1】:

    您可以使用QThread

    虽然存在其他选项,例如 QConcurrent,但也有 question its implementation 的选项。

    创建一个从 QObject 派生的对象,它将负责更新坐标。每次更新后,对象可以发出一个带有坐标的信号,然后在主线程上接收回。

    class Worker : public QObject 
    {
        Q_OBJECT
    
    public:
        Worker();
        ~Worker();
    
    public slots:
        void process();
    
    signals:
    
        typedef QVector<QPoint> PointList;
    
        void NewCoordinates(PointList points);
    
    private:
        PointList m_pointList;
    
    private:
    };
    
    
    Worker::Worker() {
        // you could copy data from constructor arguments to internal variables here.
    }
    
    // --- DESTRUCTOR ---
    Worker::~Worker() {
        // free resources
    }
    
    
    // --- PROCESS ---
    // Start processing data.
    void Worker::process() {
    
        // calculate new coordinates...
    
        emit NewCoordinates(m_pointList);
    }
    

    从主线程设置和启动线程和工作实例..

    QThread* thread = new QThread;
    Worker* worker = new Worker();
    worker->moveToThread(thread);
    
    connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), worker, SLOT(process()));
    
    // tidy up
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    
    // assuming we're in the MainWindow class, with a NewCoordinates slot function
    // collect points - C++ 5 connect syntax
    connect(worker, &Worker::NewCoordinates(Worker::PointList), this, &MainWindow::NewCoordinates(Worker::PointList);
    
    // let's go
    thread->start();
    
    void MainWindow::NewCoordinates(Worker::PointList pointList)
    {
       // handle updated coordinates
    }
    

    要了解如何真正、真正地使用 QThread,有一篇很棒的文章 here

    【讨论】:

      【解决方案2】:

      您可以使用QtConcurrent::run 在单独的线程中运行函数。

      QFuture<void> future = QtConcurrent::run(this,&GLWidget::updateCoordinates );
      

      在您的 updateCoordinates() 函数中,您可以发出一个信号,例如名为 repaint 的信号,该信号连接到 QGLWidget::updateGL 以调用 paintGL() 函数:

      void GLWidget::updateCoordinates()
      {
          while(true)
          {
              m_offset += 0.0001;
              emit repaint();
          }
      }
      

      注意repaint()信号应该连接到updateGL,连接类型为Qt::BlockingQueuedConnection

      您的 while 循环中还应该有一个标志,该标志被检查以终止 while 和线程。

      您可以检查由未来表示的异步计算的状态,例如:

      if(future.isRunning())
      {
          // It is currently running
      }
      

      或者等待它完成:

      future1.waitForFinished();
      

      【讨论】:

      • 感谢您的回复@Nejat。我按照您的建议设置了所有内容:'connect(this, SIGNAL(repaint()), this, SLOT(paintGL()), Qt::BlockingQueuedConnection);'现在 paintGL() 和 updateCoordinates() 在不同的线程中交替。那挺好的。然而,奇怪的是,三角形图形输出没有得到更新。当我调整窗口大小时,我只能看到三角形移动。你知道为什么会这样吗?
      • @user2926577 您应该将信号连接到QGLWidget::updateGLupdateGL 将致电 paintGL。我已经更新了答案。
      • 谢谢@Nejat,这行得通。我现在遇到的问题是,由于两个线程的执行是交替的(paintGL 和 updateCoordinates),我只能得到 60Hz 的整体更新率,这违背了使用线程的目的。当我删除 Qt::BlockingQueuedConnection 时,每 1000 个更新坐标获得一个paintGL,总更新率为 80kHz(千!)。但是,现在我不能再使用/单击主窗口中的按钮(沙漏光标)。我该如何解决这个问题? - 非常感谢您的建议!
      • 当您删除Qt::BlockingQueuedConnection 时,连接类型变为Qt::QueuedConnection 并且每个发射都插入到队列中,队列的大小会随时间增加。可能是因为排放量很大。
      猜你喜欢
      • 2021-06-30
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2015-08-05
      • 1970-01-01
      • 2017-04-06
      • 2018-07-27
      • 1970-01-01
      相关资源
      最近更新 更多