[Qt-interest] Long Calculations, QThread Event Loop, Signals & Slots

Etienne Sandré-Chardonnal etienne.sandre at polytechnique.org
Mon Jan 17 22:43:11 CET 2011


There may be an alternative solution :

You still split your processing in slices as in André's example, each slice
being processed by a slot (for example : compute() )
You create a signal, nextCompute()
You connect nextCompute to compute using a *queued connection*. (standard
connection will not work)
Then, at the end of compute(), you do "emit nextCompute"

In that ways, each computation slice queues the next one when it finishes.
As this is queued, any slot such as cancel() triggered during the slice
computation will emit before the next slice is computed.

I've never tried this, so it may be totally stupid. But I did not yet find
any reason it should be. It seems quite clean.

Etienne


2011/1/17 Andre Somers <andre at familiesomers.nl>

> Hi,
>
> Op Ma, 17 januari, 2011 8:20 pm, schreef Michael Jackson:
> (...)
> > There is still a problem where my calculation object is NOT getting a
> > signal
> > until the long running calculation object is done, at least with
> > QueuedConnections. So here is my general setup:
> >
> > class LongCalc {
> >    void compute() {
> >         // Stuff that takes 10 minutes to run
> >     }
> > };
> >
> > Class MyMainWindow : public QMainWindow ....
> > {
> >
> > void on_GotBtn_clicked()
> > {
> >     t = new Qthread();
> >     calc = new LongCalc();
> >     calc->moveToThread(t);
> >   // When the thread starts its event loop, start the LongCalc going
> >   connect(t, SIGNAL(started()),  calc, SLOT(compute()));
> >
> > //When the LongCalc ends then tell the QThread to stop its event loop
> >   connect(calc, SIGNAL(finished() ),  t, SLOT(quit()) );
> >
> > // When the QThread finishes, tell this object that it has finished.
> >   connect(t, SIGNAL(finished()),  this, SLOT( rec_ThreadFinished() ) );
> >
> >   connect(this, SIGNAL(sig_CancelWorker() ),
> >           calc, SLOT (on_CancelWorker()) );
> >
> >     t->start();
> > }
> >
> > private:
> >  QThread* t;
> >  LongCalc* calc;
> >
> > };
> >
> > If the "sig_CancelWorker()" signal gets emitted then the slot isn't
> called
> > until AFTER the "compute()" method has finished. Thinking through what is
> > likely going on makes sense (I think). QThread.run() is called which
> > starts
> > the event loop for that thread. The QThread then sends out the
> "started()"
> > signal which the LongCalc has hooked up to a slot (compute()) so the
> > "Compute()" method now runs which effectively blocks the event loop from
> > running? Is that correct?
> Yes, that diagnosis is correct.
>
> > I thought I knew what I was supposed to do but
> > now
> > it seems like a need yet a third thread to actually run the long
> > computation
> > while I have an intermediate thread that has an event loop that is
> running
> > to deliver the messages to the third thread?
>
> No, that would not help.
>
> >   My mind wants to create some sort of "Queue" system like a
> > Producer-Consumer" so that the slot can return right away and then ask a
> > the
> > other sleeping thread to wake up and calculate. This just smells like I
> am
> > missing something obvious with Qt and Threads.
> Indeed. You're talking about reimplementing a rudimental eventloop in your
> calculation. Not a good plan usually.
>
> >   More thoughts and clarifications would just be wonderful.
>
> There are several solutions to your problem. The first is the simplest.
> Just create a cancel flag in your worker object that is checked regulary
> in the calculation. You set it from outside your thread using a direct
> method call or a signal-slot connection with the Qt::DirectConnection
> flag. Synchronization doesn't matter here, all that can happen is that
> your worker just passed checking the flag when you set it, which causes
> one extra calculation step. No biggy, just don't expect the thread to
> terminate immediately, only quickly.
>
> The other solution is to make sure your eventloop *does* run. You can do
> that by splitting the calculation into pieces, and returning to the
> eventloop after each piece. You can trigger the next piece by using a
> singleshot, 0ms timer for instance. Alternatively, you could use
> processEvents on your thread local eventloop. I am not a big fan of this
> method though.
>
> Note that neither of these simplify the first solution though. You will
> still need a cancel flag. As long as you don't expect much communication
> from your main thread towards the worker thread than a cancel flag, the
> first solution will work fine. Note that you can still send event from the
> worker object to the GUI thread (for things like progress updates, for
> instance). As long as the GUI thread is not blocked, they will arrive just
> fine.
>
> Also note that if you have one big, blocking operation, the approach of
> moving a QObject on a QThread doesn't help you a lot. A QThread subclass
> with a reimplemented run() method that simply does the long calculation
> and without an eventloop works just as well.
>
> André
>
> _______________________________________________
> Qt-interest mailing list
> Qt-interest at qt.nokia.com
> http://lists.qt.nokia.com/mailman/listinfo/qt-interest
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.qt-project.org/pipermail/qt-interest-old/attachments/20110117/e3b96df3/attachment.html 


More information about the Qt-interest-old mailing list