PDA

View Full Version : How to seperate my program into two threads



Berryblue031
3rd March 2011, 12:38
I have a program that is separated into two projects the GuiLayer and the Datalayer both are QT based the Datalayer is compiled into a .dll that the guilayer references and objects are passed between them.

For example the user requests a search, the guilayer creates a searchObject fills in the query parameters passes it to the datalayer, which then makes a request on the server (though later it will optionally query a local database), when result returns the datalayer sets the results on the searchobject and the searchObject emits a signal telling the gui layer results are available.

The problem I have run into is the Datalayer does a lot of xml parsing and while it's parsing xml it blocks gui updates, the cursor in the lineedit stops blinking, you can't move the program etc, so I would like to have the datalayer run in it's own thread so that things like that can't happen but I am not exactly sure how to do that.

high_flyer
3rd March 2011, 13:44
Sounds like a case for QtConcurrent (http://doc.qt.nokia.com/latest/qtconcurrent.html)

MarekR22
3rd March 2011, 15:53
It is even simpler. If you created nice QObject for data layer with signals and slots then you just QObject::moveToThread giving as argument standard thead (no subclasing is needed) then do needed connections between data layer and user UI.
Thats is all you need to do :).

Added after 4 minutes:

simple example:

MainWindow::MainWindow(QObject *parent) : QMainWindow(parent)
{
QThread* thread = new QThread(this);
datalayer = new DataLayerClass();
datalayer ->moveToThread(thread);
datalayer->setParent(this); // parent can't be set before moveToThread

connect(this, SIGNAL(sendRequest(QString)), datalayer, SLOT(startRequest(QString)));
connect(datalayer, SIGNAL(someResultReady1(QString)), this, SLOT(setLabel(QString)));
connect(datalayer, SIGNAL(someResultReady2(QString)), this, SLOT(setEdit(QString)));
}

wysota
3rd March 2011, 22:03
I personally think using QtConcurrent is simpler. Here there is a problem what to do with the thread once you're done with it.

By the way, I think line #6 in your code is invalid. What would happen if "this" was deleted?

MarekR22
4th March 2011, 08:25
By the way, I think line #6 in your code is invalid. What would happen if "this" was deleted?
Yep you are rigth! Thread pointer should be stored as a field and then in destructor:

MainWindow::~MainWindow()
{
thread->exit();
thread->wait(2000);
thread->deleteLater();
}

Anyway can you give this simple example with QtConcurrent?

wysota
4th March 2011, 10:28
Anyway can you give this simple example with QtConcurrent?

It could be something like this:

Result parseXml(const QString &xml) { ... }
// ...
QString xml = ...;
QFuture<Result> result = QtConcurrent::run(parseXml, xml);

// optionally:
QFutureWatcher<Result> m_watcher;
m_watcher.setFuture(result);
connect(&m_watcher, SIGNAL(finished()), ..., ...);

Result res = result.result(); // or m_watcher.future().result();
The main advantage over your solution is that if you have a machine with 4 cores, you can run 4 such functions concurrently and with your solution you only have one thread so requests are queued and some CPUs are left idle. Besides I think using QtConcurrent is just much simpler :) Especially that you don't need the optional part since calling QFuture::result() will block if the result is not ready yet. So you can even have a flow such as this:

QFuture<...> future = QtConcurrent::run(something); // start calculations you'll need later
// more code here to prepare final operation
Result result = future.result(); // may block
doSomethingWith(result, otherThings);

yeye_olive
4th March 2011, 14:43
@wysota

I believe lines 8 and 9 of your code should be swapped, according to the documentation (http://doc.qt.nokia.com/latest/qfuturewatcher.html#setFuture).

Berryblue031
10th March 2011, 14:31
hmmm what about using a QRunnable for the parser?



class MyXmlParser : public QRunnable
{
void run()
{
//parse
searchObject->setResults(parsingresult); //This causes the searchObject to emit the signal that the guilayer is waiting for
}
};

QThreadPool::globalInstance()->start(new MyXmlParser(searchObject, toBeParsedString));

Are there any other big downsides to this solution? I think it is very similar to what is done with QtConcurrent::run?

I was thinking to run the datalayer completely in it's own thread (there can only ever be one datalayer object) then change the gui/datalayer communication to be purely signal/slot based and within the datalayer itself move the parsing code into runnables. Then I can be completely sure the datalayer never blocks the gui no matter who puts code into it.

When using runnables is it necessary to make any calls to moveToThread(); I am honestly a bit confused on when you need to use that function.