1

Background

I have a QObject (myClass) that runs in its own thread. The UI can trigger a time consuming slot in myClass. This slot will process stuff, update the UI, process more stuff then updates variables used for background processing. If the slot is triggered faster than it can process, the first UI update will occur fast, but subsequent updates will have to wait (as myClass is still bust processing y() & z()).

void myClass::mySlot() {
    x(); // Fast
    emit updateUI();
    y(); // Slow
    z(); // Slow
    emit processingComplete();
}

To overcome this I've used QtConcurrent::run for the slow processing so that subsequent updates also update the UI fast (as they don't have to wait for for y() & z()).

void myClass::mySlot() {
    x(); // Fast
    emit updateUI();

    // future is a member variable of myClass of type QFuture<void>
    future = QtConcurrent::run([&]() {
        y(); // Slow
        z(); // Slow
        emit processingComplete();
    });

}

I've tried to make the code cleaner by using cancel() if the thread is still running but it fails as "... the future returned by QtConcurrent::run() cannot be canceled"

    ...
    emit updateUI();

    if(future.isRunning())
        future.cancel();
    future.waitForFinished()

    future = QtConcurrent::run([&]() {
    ...

Questions

  1. Is reassignment of a running QFuture<T> safe when using QMutexLocker? This answer states that it's safe but doesn't cover mutexes.
  2. Will the "overwritten" threads still execute in the background, orphaned, until they time out or will they stop executing regardless of the line they're on?
  3. Can a QFuture<T> be restarted from the beginning of its execution (if yes, can it be restarted regardless of its state, running/finished/canceled/paused)?
  4. Are there other workarounds if reassigning isn't safe? (I can't use this solution as y() & z() are linear, not loops).

Assume the above code is the only place that accesses the future variable.

Roqux
  • 608
  • 1
  • 11
  • 25
  • To clarify, the `x` slot may be triggered while `y` and/or `z` are running, and in that case, you want to cancel `y`/`z` and run `x` instead? Are there any dependencies between the different slots? – bnaecker Jan 07 '20 at 04:46
  • "Is reassignment of a running QFuture safe when using QMutexLocker?" Where is the locker used, on which mutex? In general, a future is a handle to the result and not the thread itself, so copying or destroying it doesn't affect the operation itself. Note also that futures from run() cannot be cancelled, see docs of QFuture::cancel() (a plain function/lambda cannot be safely cancelled in the general case) – Frank Osterfeld Jan 07 '20 at 06:32
  • @bnaecker sorry for the confusion. `slowProcess()` is the slot, with `x`, `y` & `z` being functions called from within the slot. When `slowProcess()` is triggered, it should start executing `x`, whereas the qthread may still be busy executing the previous call to `slowProcess()`, queuing this new call. (I've updated `slowProcess()` to `mySlot()`) – Roqux Jan 07 '20 at 20:05
  • @FrankOsterfeld The locker will be called with the same mutex each time, within `y`, `z` and `myOtherSlot`. Just clarifying, _"so copying or destroying it doesn't affect the operation itself"_ can be interpreted for Q2 as "yes, they will continue executing in the background, until their execution finishes or they time out, even though the future has been reassigned"? – Roqux Jan 07 '20 at 20:21

0 Answers0