Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread count increases a lot, even when deleting the threads

Have an application where I have QOBJects which all contain an QNetworkAccessManager. I am aware of that it's suggested to only have on per application but since I'm making a lot more that 6 calls at the same time, I needed to have it like this. So, this is how I start the threads.

FileUploader *fileUploader = new FileUploader(_fileList);
QThread *fileUploaderThread = new QThread();
fileUploader->moveToThread(fileUploaderThread);

// uploader > model
connect(fileUploader, SIGNAL(progressChangedAt(int)), _model, SLOT(reportProgressChanged(int)), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(statusChangedAt(int)), _model, SLOT(reportStatusChanged(int)), Qt::QueuedConnection);
// uploader > its thread
connect(fileUploader, SIGNAL(canceled()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
// uploader > this
connect(fileUploader, SIGNAL(canceled()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finishedCurrentUpload()), this, SLOT(uploadNextFileOrFinish()), Qt::QueuedConnection);
// thread > this
connect(fileUploaderThread, SIGNAL(finished()), this, SLOT(checkIfAllThreadsAreFinished()), Qt::QueuedConnection);
connect(fileUploaderThread, SIGNAL(finished()), this, SLOT(deleteFinishedThread()), Qt::QueuedConnection);
// this > uploader
connect(this, SIGNAL(cancel()), fileUploader, SLOT(cancel()), Qt::QueuedConnection);

fileUploaderThread->start();
QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);
QMetaObject::invokeMethod(fileUploader, "uploadAt", Qt::QueuedConnection, Q_ARG(int, startIndex));

QMutexLocker locker(&_mutex);
_threadCount++;

Every thread starts out with an index to a list so that they can fetch the thing they need to upload and proceed about 5 steps (calls with the QNetworkAccessManager). When there's no more items to upload, the fileUploader signals "finished()" which calls the deleteFinishedThread and deleteFinishedUploader where I do:

QThread *thread = qobject_cast<QThread*>(sender());

if(thread != NULL) thread->deleteLater();

or

FileUploader *fileUploader = qobject_cast<FileUploader*>(sender());

if(fileUploader != NULL) fileUploader->deleteLater();

These are suppose to delete the threads when they are done.

The issue is that every time I start (for example) 3 threads that have 1 file to upload and handle each, the thread count gets increased by 8-10. This means that the thread count goes from about 5 to 100 if I restart the uploading process a few times.

What am I doing wrong? Or is my biggest issue that I use "Windows Task Manager" to control this? I am handeling all replies from the QNAM which I delete and everything seems to get deleted, but still I scratching my head when the threadcount keeps increasing...

EDIT: In my fileuploader I create an object(Manager) on the heap which has a QNetworkAccessManager on the stack. When the fileuploader gets deleted it calls "deleteLater()" on the Manager but it never gets deleted. We tried to delete the Manager and set it to NULL but that gave us an access violation since the Manager wasn't done yet (the QNetwork.dll reported the issue so it must be something inside the QNAM that is running still). The times when we didnt get access violation, the object was deleted and the thread count went back to normal. What can live inside the QNAM and prevent me from deleting it when it goes out of scope? Should I create the QNAM on the heap instead? At this stage non of the destructors are being called even when calling deleteLater()...

Also, how do I reduce the handle-count?

like image 931
chikuba Avatar asked Apr 17 '12 02:04

chikuba


2 Answers

I might be wrong, but I think there is a problem with your signals:

// uploader > its thread
connect(fileUploader, SIGNAL(canceled()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
// uploader > this
connect(fileUploader, SIGNAL(canceled()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);

Remember that when multiple slots are connected to the same signal, they're executed in the order of connection. Here, when the fileUploader will be finished, it will call finished() which will first call the quit() method of the thread, and then the deleteFinishedUploader() method. Same for the canceled() signal. But, meanwhile, the thread was finished, so no event processing for the fileUploader can be done (consequence of the moveToThread(...)). deleteLater() needs event processing, thus your fileUploader will never be deleted...

I am not 100% that arranging your connections in the other way will get things working: the deleteLater() can be called and the thread exited immediatelly after, without event processing.

The solution might be to re-moveToThread() the fileUploader to the main thread, or to a thread which still processes its event loop.

like image 67
Synxis Avatar answered Sep 25 '22 15:09

Synxis


Not an answer but :

    fileUploaderThread->start();
    QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);
    QMetaObject::invokeMethod(fileUploader, "uploadAt", Qt::QueuedConnection, Q_ARG(int, startIndex));

means you start the even loop, and then you queue up slots or signals to be executed.. Assume (in general) that there are other QObject in this thread. It might be possible that these get their slots or signals executed because the event loop has already started. If you want the "init" and "uploadAt" to be the first methods to be invoked when the event loop run, you queue them before starting the event loop (If the thread is not started they are never going to be executed).

From QMetaObject::invokeMethod:

If type is Qt::QueuedConnection, a QEvent will be sent and the member is invoked as soon as the application enters the main event loop.

In this case the event is sent to the thread event loop.

like image 42
UmNyobe Avatar answered Sep 22 '22 15:09

UmNyobe