Crash when updating QGraphicsScene in a thread
-
On Windows 7 64 bit using Qt 4.8.4 I try to defer the updating of a pretty big QGraphicsScene to a thread, so that the main app can immediately continue. I want the drawing to take place in the background.
I wrote a little test app to reproduce the problem. The test app also produces a crash, but a different one from the main app. The test app crashes on application exit, the message says something about sending signals to objects belonging to a different thread. In my real application it happens on scene->clear(). I hope the two effects are related, so what's wrong in the following code (Just remove the two slashes in front of "#define crash'):
File mainwin.h:
@class mainWin : public QMainWindow {
Q_OBJECTpublic:
mainWin(QWidget *parent = 0) : QMainWindow(parent) {
QWidget *widget = new QWidget(this);
QVBoxLayout *layout = new QVBoxLayout(widget);
QPushButton *btn = new QPushButton("Redraw");view = new QGraphicsView(widget); scene = new QGraphicsScene(this); layout->addWidget(view); layout->addWidget(btn); scene->addText("Test"); view->setScene(scene); setCentralWidget(widget); connect(btn, SIGNAL(clicked()), this, SLOT(redraw())); countClicks = 0; }; ~mainWin() {}; virtual void doRedraw() { scene->clear(); scene->addText("Test " + QString::number(countClicks++)); };
public slots:
virtual void redraw() {
//#define crash
#ifdef crash
QtConcurrent::run(this, &mainWin::doRedraw);
#else
doRedraw();
#endif
};private:
QGraphicsView *view;
QGraphicsScene *scene;
int countClicks;
};
@File main.cpp:
@#include "mainwin.h"int main(int argc, char *argv[])
{
QApplication a(argc, argv);
mainWin *w = new mainWin;
w->show();
a.exec();
}
@ -
QGraphicsScene (and most GUI classes) are not thread-safe. They must be used from the main thread only, i.e. painting must be done in the main thread (that's why it's also called the "GUI thread").
In your code, your QGraphicsView could be trying to read the QGraphicsScene, while the latter is being updated in the other thread -- thus you get data corruption.
Anyway, QGraphicsScene is supposed to be highly-optimized to handle a large number of graphical objects. How bad is the delay in your app? One option is to keep the painting in the main thread, and move data-processing to a secondary thread.
-
Drawing the scenes (actually there are four scenes with ~300 objects in each) takes about 4 seconds. Drawing does next to no calculation (just a few trivial sums and comparisons). The delay is not huge, but the user can feel it and he thinks that it's not necessary, since drawing in a background process should really be possible.
At this point I guess that double buffering should be the easiest way to do this. The background thread draws to a second set of scenes (which is not connected to the view) and on completion the main thread switches scenes.
Would this be possible (are scenes thread-safe while not connected to the UI?), or is there an even simpler way to accomplish what I need?
-
Keep in mind that when using signals and slots to communicate between threads, the default connection is queued, which is dreadfully slow, I've had cases where multi-threaded computation were totally crippled by the bottleneck of signals and slots. Multithreaded workloads must not be very fine grained, and whenever possible avoid signals and slots for transferring data and instead use mutexes (or .. mutices???) and locks, leave signals and slots... well for signaling.
-
[quote author="utcenter" date="1361099369"]Keep in mind that when using signals and slots to communicate between threads, the default connection is queued, which is dreadfully slow, [..] whenever possible avoid signals and slots for transferring data and instead use mutexes (or .. mutices???) and locks, leave signals and slots... well for signaling.[/quote]
Thanks a lot for your point about preferring the classic means for data transfer - I'd probably have gone for signal/slots first, as you guessed.
Btw, I just added a little counter to my scene drawing - it's actually over 2000 objects in total (mostly texts and boxes around them, with quite a lot of styling like different fonts, sizes and alignments), so I guess 3 seconds isn't really that bad (and on a 5 years old computer too). If I had to do that from scratch again I'd probably use the designer and place a lot of labels in boxes, because the layout is static, then I'd just have to update label texts when data changes. OTOH I feel my approach is more flexible, I have more freedom to place things outside the pattern where some Layout would put them.
P.S. I'd guess "mutexes" is correct, but "mutices" sounds much better - I'm going to use that in the future :)
-
Would you be willing to show us the contents of your redraw() function?
-
[quote author="JKSH" date="1361103013"]Would you be willing to show us the contents of your redraw() function?[/quote]
Although I appreciate your interest very much and would be interested in discussing more details with you, I fear I can't do that easily.