Freeze in multithreaded program
-
Hi,
I've been writing a small update software for a video game, using Qt 4.8. The software features a FTP client, a basic UI a some extensions (launch the game, etc). The source code is available on Github : https://github.com/arbonagw/UnrealUpdater .
I'm experiencing weird UI freezes where it should not, so I'll explain how it works.I've got a main class, Updater, that launches a second thread with a worker class, Downloader, that uses QNetworkAccessManager to download files. Here is the process :
- it starts by downloading a manifest file with a tree of file names along with MD5 hash ;
- then it computes a list of files to update by checking each of these files locally with their MD5, this work is done with QtConcurrent::run() ;
- after that, Updater sends a file name, Downloader signals when it's done, and so on.
- when the list of files is empty, it starts the game.
Now I've got three problems :
- while computing the MD5, checking the files, the UI is frozen. There is just no reason, it's supposed to be done in a separate thread ;
- -while downloading, I receive the "finished" event from QNetworkAccessManager in multiple occurrences. The first finished() is sent once, the second is sent twice, and so on. What he hell ?- (my bad, found that out)
- the downloading process looks prone to freezing randomly while downloading (no downloading, but a responsive UI that can open dialogs when pressing buttons).
I fail to understand how this is is failing. Can anyone help with the software structure ? What is actually wrong here ?
Thanks !
-
See at this:
@
QFuture<void> parser = QtConcurrent::run(this, &Updater::GetFilesToDownload, *dom, QString(""));
parser.waitForFinished();
@you block your main thread waiting for computation result. You should use QFutureWatcher to make it asynchronous and non-blocking.
-
Edit: And Bogdan has beaten me to it :D
Your code has
@
QFuture<void> parser = QtConcurrent::run(this, &Updater::GetFilesToDownload, *dom, QString(""));
parser.waitForFinished();
@...and it looks like the hashing is done within Updater::GetFilesToDownload().
Yes, Updater::GetFilesToDownload() is being run in other threads, BUT you've asked your program to wait until all downloading and hashing have finished. So, the main thread (which handles the GUI) will wait -- i.e. freeze -- while your hashes are being computed.
Here's a possible solution: Instead of using waitForFinished(), attach a QFutureWatcher to your QFuture, and let your main thread continue running. Then, get the QFutureWatcher to emit a signal once your hashes are done. Qt is an event-driven framework -- use that to your advantage :)
-
Wow thanks ! I understand, I did not think of that.
I just fixed my code, this problem is gone now. One down !@QFutureWatcher<void>* watcher = new QFutureWatcher<void>();
connect(watcher, SIGNAL(finished()), this, SLOT(StartDownload()));
QFuture<void> parser = QtConcurrent::run(this, &Updater::GetFilesToDownload, *dom, QString(""));
watcher->setFuture(parser);@There is one issue left - the download stops randomly. I added some qDebug here and there, I find that I just stop receiving events from QNetworkAccessManager. It can't be a Qt bug since I was isung QFtp before...
I also have minor freezing when moving the window around while downloading so I guess there is more failure where the first one came from...
-
How many requests do you send to the server at a time? I've found that if I send too many requests simultaneously, strange things happen to the connection. For one of my programs, I ended up queueing my downloads manually and making sure that only one file is being downloaded at a time. It's slower, but much more stable.
-
[quote author="GwennH" date="1367224499"]I only send one request at a time and wait for the answer.
Now, I do have a workaround for the problem (reset the downloader and restart on timeout) but that's hardly a fix...[/quote]I think that's a generic networking issue. Sometimes my browser download stalls too, and needs to be restarted. My workaround for that is to use a download manager (but I don't think QNetworkAccessManager has one built-in, unfortunately) -
Yes, that's quite right (also the reason why there was a restart system in the first place). But I'm still surprised by how it happens (no error given by Qt, just no packets received) and it happens a lot on a server which has tremendous reliable bandwidth but laggy interface. My guess was that the UI and networking were not well separated, but I can't prove that...