0
votes

I have inherited a complex program in my current job and am seeking to reduce image flickering from a stream of data coming over a QTcpSocket.

The program receives the continuous stream of data, processes it, then paints it on the screen with a paintEvent.

The processing function is run based on a signal/slot connection where the signal is readyread() from a QTcpSocket, and the slot is the data processing function. The stream is continuous so this signal/slot is continually firing and updating the painted image on the screen based on the incoming data.

The image flickers constantly, and I assume that the processing in the main event loop could be interfering with the data stream, so my idea was to put the data processing function in its own thread. This data processing function is so thoroughly integrated into the other features of the program, that subclassing the data stream at this point so that I could apply a QThread is not a solution and would be a complete restructure of the entire program, taking tons of time.

So my idea was to use QtConcurrent like so:

void MainWindow::getDataThread(){     //implemented as a slot 
    wpFuture = QtConcurrent::run(this, &MainWindow::getData);
}

where getData() is the data processing function connected to the readyread() signal:

connect(tcpSocket2, SIGNAL(readyRead()), this, SLOT(getData()));

So I replaced SLOT(getData()) with SLOT(getDataThread()) to allow the data processing function to be run on a new thread that is obtained from the global thread pool. Since the stream is continuous, I believe it is constantly assigning a new thread every time the getData processing function is ran. It does seem to reduce flickering but after about 30 to 60 seconds the program randomly crashes with no specific callouts.

So my question is: Is there a better method for threading my data processing function, without subclassing the data stream? Is my thinking/understanding wrong in my implementation of QtConcurrent in this specific situation?

Thank you.

1
usually flickering is solved with Double Buffering: doc.qt.io/archives/qq/qq06-flicker-free.htmlWindyFields
Also, are you sure, that calling MainWindow::getData from multiple threads simultaneously is safe? If your program used to be single threaded, probably it was not intended to be thread safe.WindyFields
Thanks, I'll check into the double buffering. Regarding the multiple threads, the QtConcurrent::run was getting executed every time data was detected on the socket. It is my understanding that once QtConcurrent has run the function on a thread and the function has completed, it returns the thread to the global threadpool, so each iteration would assign thread to function, release thread, assign thread, release. This would mean only one thread would be running for the MainWindow::getData function at any one time. I assume this wasn't the best approach.Link H.

1 Answers

0
votes

From your comment I assume your understanding of thread pool is wrong.

There are a number of threads in a thread pool. Each time you call QtConcurrent::run a free thread from the global thread pool is taken and being handed a task to do (MainWindow::getData). If you call QtConcurrent::run several times than every time MainWindow::getData will be executed in (presumably) different thread. If there are no currently available threads in thread pool, you tasks will be queued and handed to threads as they become available later. This way you can have several simultaneous tasks running limited by the number of threads in the thread pool.

Now the problem is, that MainWindow::getData is probably not thread safe by its design. QtConcurrent::run(this, &MainWindow::getData); called several times may result in data race.

If you want a separate single thread to process data then just use QThread (no need to "subclass" anything):

// A thread and its context are created only once  
QThread thread;
QObject context;
context.moveToThread(&thread);
// ...

QObject::connect(tcpSocket2, &QTcpSocket::readyRead, &context, [this] () { 
    this->getData(); 
}, Qt::QueuedConnection);

thread.start()

Now as long as context object is alive and thread is running each time QTcpSocket::readyRead is emmited - the lambda will be executed.

Still pay attention so that your worker thread and you main thread do not collide in getData.