I'm trying to slog my way through building an application to talk to a linescan camera. Ultimately, I want to pass a "block" (i.e., array) of 384x128 unsigned short
values every 100ms from a QThread
(data acquisition) to a QRunnable
(data processing). This means the QRunnable
will have 100ms to process the data before the next block arrives.
I'm still not sure the right way to move the data around. Right now, I'm using a QVector. In Qt4, I understand implicit sharing to mean that a QVector would not be copied if emitted in a signal, until the object is written upon. However, in a small test application I made, I am not sure I understand exactly what that means. Here is the output of the MWE provided below...
Acquire thread: init.
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}
Acquire thread: block acquired: 0x106485e78 Content: {1, 2, 3, 4}
GUI thread: received signal: 0x7fff5fbfda98 Content: {1, 2, 3, 4}
I am using a "dummy" QVector with four values and tracking the address of the vector as the thread runs. The data is correct throughout, but it seems a copy is made. I am not changing the data at any point in the application... just displaying. I've tried using const QVector<unsigned short>
throughout, various iterations of using references, etc. The address always changes. Since performance will be important here, I'm concerned about making copies of the QVector when 384*128 values.
Also, in another SO question, I am struggling with figuring out how to have the QRunnable receive the data (that is all left out of this example). But, that is important to note here because my idea is to have the QRunnable operate on the reference to the image_buffer
that lives in the QThread. I just haven't figured out how to do that.
The specific questions:
-- Why does it appear a copy is made? Is this because of multithreading?
-- Do I have to explicitly use references (i.e., image_buffer&) throughout, thereby removing QVector entirely? Or, should I worry about the copies?
-- What is the right way to pass data from QThread to QRunnable for my situation?
MWE below to demonstrate the different addresses. Disclaimer: I'm learning C++ as I go. No formal training, just a few good books in front of me.
main.cpp
#include <QApplication>
#include <QMetaType>
#include <QVector>
#include "appwidget.h"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
AppWidget gui;
gui.show();
qRegisterMetaType<QVector<unsigned short> >("QVector<unsigned short>");
return app.exec();
}
** appwidget.h **
#ifndef APPWIDGET_H
#define APPWIDGET_H
#include <QWidget>
#include <QVector>
#include "acquire.h"
class AppWidget : public QWidget
{ Q_OBJECT
public:
AppWidget(QWidget *parent = 0);
protected:
Acquire thread;
public slots:
void processBlock(QVector<unsigned short>);
};
#endif
appwidget.cpp
#include <QtGui>
#include <iostream>
#include "appwidget.h"
AppWidget::AppWidget(QWidget *parent)
: QWidget(parent)
{
thread.liftoff();
connect(&thread, SIGNAL(blockAcquired(QVector<unsigned short>)), this, SLOT(processBlock(QVector<unsigned short>)));
setWindowTitle(tr("TestApp"));
resize(550, 400);
}
void AppWidget::processBlock(QVector<unsigned short> display_buffer)
{
std::cout << "GUI thread: received signal: "
<< &display_buffer
<< " Content: {"
<< display_buffer.at(0) << ", "
<< display_buffer.at(1) << ", "
<< display_buffer.at(2) << ", "
<< display_buffer.at(3)
<< "}" << std::endl;
}
acquire.h
#ifndef ACQUIRE_H
#define ACQUIRE_H
#include <QVector>
#include <QThread>
class Acquire : public QThread {
Q_OBJECT
public:
Acquire(QObject *parent = 0);
~Acquire();
QVector<unsigned short> display_buffer;
void liftoff();
signals:
void blockAcquired(QVector<unsigned short>);
protected:
void run();
private:
};
#endif
acquire.cpp
#include <iostream>
#include <time.h>
#include <stdlib.h>
#include "acquire.h"
Acquire::Acquire(QObject *parent)
: QThread(parent)
{
}
Acquire::~Acquire()
{
std::cout << "Acquire thread: dying." << std::endl;
wait();
}
void Acquire::liftoff()
{
std::cout << "Acquire thread: init." << std::endl;
start();
}
void Acquire::run()
{
QVector<unsigned short> display_buffer(2 * 2);
forever {
/*
display_buffer will ultimate be a memcpy of image_buffer
.. image_buffer updated continuously, 1 line every 800000ns x 128 lines
*/
display_buffer[0] = 1;
display_buffer[1] = 2;
display_buffer[2] = 3;
display_buffer[3] = 4;
nanosleep((struct timespec[]){{0, 800000*128}}, NULL);
std::cout << "Acquire thread: block acquired: "
<< &display_buffer
<< " Content: {"
<< display_buffer.at(0) << ", "
<< display_buffer.at(1) << ", "
<< display_buffer.at(2) << ", "
<< display_buffer.at(3)
<< "}" << std::endl;
emit blockAcquired(display_buffer);
}
}
&display_buffer
returns the pointer to the object itself. It's not surprising that it will be different in your case. If you want a pointer to the QVariant's internal array, usedisplay_buffer.constData()
. – Pavel StrakhovAcquire thread: block acquired: 0x105501010
||GUI thread: received signal: 0x105501010
||Algorithm thread: 0x105501010
– ph0t0n