Strange errors from QThread
-
I have a QThread that downloads icons via cURL from a wikia, and for some reason whenever i execute the download, or skip it if the file already exists my program crashes with a weird error, theres quite a few lines of code that arent really neccessary for this error so i've tried to narrow it down to just the pertinent parts
The Thread creator
while (q.next()) { ... if (!fileExists(istemp->iconPath)) { QThread *itemRetriever = new QThread; threadsRemaining++; ItemSearchIconWorker *icw = new ItemSearchIconWorker(istemp, &threadsRemaining); icw->itemWidget = istemp; QObject::connect(itemRetriever, SIGNAL(started()), icw, SLOT(process())); QObject::connect(icw, SIGNAL(finished()), itemRetriever, SLOT(quit())); QObject::connect(icw, SIGNAL(finished()), icw, SLOT(deleteLater())); QObject::connect(itemRetriever, SIGNAL(finished()), itemRetriever, SLOT(deleteLater())); icw->moveToThread(itemRetriever); threads.insert(threads.end(), itemRetriever); connect(icw, SIGNAL(iconDLComplete()), this, SLOT(threadComplete())); totalThreads++; } else { QIcon *newIcon = new QIcon; newIcon->addFile(istemp->iconPath, QSize(28, 28)); istemp->itemIconLabel->setPixmap(newIcon->pixmap(newIcon->actualSize(QSize(28, 28)))); } index++; } for (QThread *t : threads) { t->start(); }
The Worker's proccess()
ItemSearchItem *temp = dynamic_cast<ItemSearchItem*>(istemp); //downloadFileS("abcd", "abcd"); // This usually downloads the requested image, but is commented out for testing (still breaks even without it) QIcon *tempIcon = new QIcon; tempIcon->addFile(temp->iconPath, QSize(28,28)); temp->itemIconLabel->setPixmap(tempIcon->pixmap(tempIcon->actualSize(QSize(28, 28)))); emit iconDLComplete(); emit finished();
The threadComplete slot just decreases threadsRemaining by 1, then emits lookupFinished() if its 0
lookupFinished is connected to a slot that fills a list, but it never reaches it, as it just crashes right before
The error i get when it crashes is
ASSERT failure in QVector<T>::at: "index out of range", file c:\users\qt\work\qt\qtbase\include\qtcore\../../src/corelib/tools/qvector.h, line 424 Debug Error! Program: ...x64\Debug\Qt5Cored.dll Module: 5.10.1 File: c:\users\qt\work\qt\qtbase\include\qtcore\../../src/corelib/tools/qvector.h Line: 424 ASSERT failure in QVector<T>::at: "index out of range", file c:\users\qt\work\qt\qtbase\include\qtcore\../../src/corelib/tools/qvector.h, line 424 (Press Retry to debug the application) QWidget::repaint: Recursive repaint detected
i also get this message
QBackingStore::endPaint() called with active painter on backingstore paint device
That message spams itself until i close the program, and if i trace it in debug the call stack just leads me to my programs .exec() in my main() function, and i'm not using any QVectors
And this is the call stack from the error
-
from http://doc.qt.io/qt-5/thread-basics.html
All widgets and several related classes, for example QPixmap, don't work in secondary threads.
Never manipulate the UI (
itemIconLabel->setPixmap
) or QPixmaps (tempIcon->pixmap
) from secondary threads -
In addition to @VRonin, you don't need the hundreds of threads. Either use a (thread-safe) queue for the jobs, or simply
QtConcurrent::run
to use the already existing thread pool. Threads are rather heavy on Linux, so it makes little sense to create them to fetch single item over the network and then destroy them for no obvious reason. -
Along with what @VRonin was saying about accessing (updating) the UI from other than the main thread, you must be very careful using QVector, QList, QMap, std::vector, std::list, std::map, etc. across threads as they are intrinsically NOT multi-thread safe. Synchronizing mechanisms must be employed.
Using thread pools is very effective for managing your threads and resources.
-
I feel like this is a perfect personification of the "came searching for copper but found gold meme", Sometime in the past I acknowledged that modifying UI elements in a secondary thread caused crashes, I even fixed it, then when I moved my project from computer to computer i must have copied an old file or something, and forgot that that was a no-no. But I digress, not only did this thread solve my problem, it also showed me a way to ditch cURL, and rely solely on QT, as well as circumvent the necessity of the threading in the first place, so thank you all for answering my question and more @Buckwheat / @VRonin /@kshegunov