HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

1 producer, n consumer with QThread

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withproducerconsumerqthread

Problem

I am new to C++ and to threading, so I'm sorry if my code is bad. I'm here to get it better and to learn.

Any kind of advice (both on C++ style, on Qt usage, or on threading handling) is welcome. Please feel free to comment anything, also if the question has to be reformulated.

I have this problem:

  • one thread that grab frames from a video/web cam



  • some (let's say 3) threads that take the last grabbed frame, do some computer vision analysis, and display it back in the main gui. Each computation thread could have different computation time (because of computation-heavy algorithm like optical flow or similar)



There are few important things I want to care about:

  • the threads must run in parallel (that's why I used the threads)



  • threads takes only the last frame depending on their own job, so if a thread has some heavy optical flow computation, it will take only frames (let's say 1, 15, 21, 30). Instead, a light computation that does only frame differencing will take all frames 1, 2, 3, 4,..



  • I need a way to stop it and restart it. Let's say that I have 3 video to analyze, I want to do the first, wait for everything is finished, to the second, etc..



I have done a little prototype that works with int as a shared resource, and not with a frame. I will use opencv, cv::Mat that are basically containers for uchar array where each uchar represents a pixel.

I think that this is a working prototype, but I'm very scared about threading. I've never done something with multithreading, I don't understand the problems well, and I don't know how to test it correctly.

I'm going to copy the files here, but the project is also on bitbucket.

P.S. If it's worth something, I'm using Qt 4.8 with QtCreator 2.8 under Mac OS/X 10.8, but should be super cross-compiling as I'm just using Qt objects.

main.cpp:

```
#include
#include "mainwidget.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

MainWidget w;
w.show();

return a.exec()

Solution

First of all: your code is not bad; I agree with Wayne Conrad.

I have a minor remark about the connect statement. You are currently using Qt 4.8, so this is fine as it is. However with Qt 5 a new signal/slot syntax was introduced.

Your question about threading: Don't be afraid of QThread, they're just threads too. The problem with QThread is only this: a lack of consistent documentation. You have probably looked up the documentation of QThread and ended up subclassing QThread. There's nothing seriously wrong with this approach but if you want to use the event loop it becomes difficult.

I ended up in exactly the same situation in one of my projects. Then I came across a very interesting post of Maya Posch. You may also want to read Bradley T. Hughes' post - one of the Qt core developers - where he admits the mistakes of the past (and even takes blame for it: i take my hat off to so much honesty). Debao's Blog has a good overview of the QThread history and how to do it wrong and right. (Part 2 here). He has a short conclusion:

  • Subclass QThread and reimplement its run() function is intuitive and there are still many perfectly valid reasons to subclass QThread, but when event loop is used in worker thread, it's not easy to do it in the right way.



  • Use worker objects by moving them to the thread is easy to use when event loop exists, as it has hidden the details of event loop and queued connection.



To summarize, you would subclass from QObject instead of QThread:

class Producer : public QObject
{
    Q_OBJECT
public:
    explicit Producer(Resources *r, QObject *parent = 0) 
    // ...


and then create your producer this way:

QThread* producerthread = new QThread;
Producer* producer = new Producer();
producer->moveToThread(producerthread);
connect(producer, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(producerthread, SIGNAL(started()), producer, SLOT(process()));
connect(producer, SIGNAL(finished()), producerthread, SLOT(quit()));
connect(producer, SIGNAL(finished()), producer, SLOT(deleteLater()));
connect(producerthread, SIGNAL(finished()), producerthread, SLOT(deleteLater()));

connect(producer, SIGNAL(newFrame(int)), this, SLOT(showProducer(int)));

producerthread->start();

Code Snippets

class Producer : public QObject
{
    Q_OBJECT
public:
    explicit Producer(Resources *r, QObject *parent = 0) 
    // ...
QThread* producerthread = new QThread;
Producer* producer = new Producer();
producer->moveToThread(producerthread);
connect(producer, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(producerthread, SIGNAL(started()), producer, SLOT(process()));
connect(producer, SIGNAL(finished()), producerthread, SLOT(quit()));
connect(producer, SIGNAL(finished()), producer, SLOT(deleteLater()));
connect(producerthread, SIGNAL(finished()), producerthread, SLOT(deleteLater()));

connect(producer, SIGNAL(newFrame(int)), this, SLOT(showProducer(int)));

producerthread->start();

Context

StackExchange Code Review Q#29589, answer score: 4

Revisions (0)

No revisions yet.