[Qt-interest] Long Calculations, QThread Event Loop, Signals & Slots
Andre Somers
andre at familiesomers.nl
Mon Jan 17 21:06:53 CET 2011
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é
More information about the Qt-interest-old
mailing list