1

I am working on a plotting algorithm. To do this I get the data from a DAQ board in my main GUI thread and then I send the data over to a worker thread to be processed, the worker thread emits a signal with the new QImage which I display as a plot in my GUI. The problem is the function, let's call it generateImage(), to calculate and generate the QImage takes a long time (~50-70 milliseconds, depending on data length) and in between this time another set of data might arrive which will require the worker thread to recalculate the plot from the beginning. I want the generateImage() to abandon the calculation and restart from the beginning if the new data arrives while it is still calculating. My approach is to set a member boolean variable, let's call it b_abort_, and check if it is set to true inside generateImage() and return if it's true, outside generateImage() it always remains true and I only set it to false before generateImage() is called.

All this happens in a worker thread, I subclass QObject and use moveToThread() to move it to a worker thread.

The function which starts calculation:

void WorkerThread::startCalc()
{
    b_abort_ = false;
    generateImage();
    // if b_abort_ is set to true, generateImage() will return prematurely 
    if(b_abort_)
        emit calcFinished();
    else
        b_abort_ = true;
}

Function which does all calculations and generates image:

void WorkerThread::generateImage()
{
    /* Calculation of some parts */
    for(int ii = 0; ii < Rawdata.length(); ++ii) // Starting main time consuming loop
    { 
        if(b_abort_)
            return;
        /* Perform calculation for one data point from Rawdata */
    }

    // data calculation complete, now it's time to generate QImage
    // before that I check if b_abort_ is set to true
    if(b_abort_)
        return;

    for(int ii = 0; ii < CalculatedData.length(); ++ii) // plotting calculated data on QImage
    {
        if(b_abort_)
            return;
        /* plot one data point from CalculatedData vector */
    }

    // generation of QImage finished, time to send the signal
    emit renderedPlot(image); // image is a QImage object
}

In my worker thread, I have a slot to receive data from the main GUI Thread, it is configured with Qt::QueuedConnection (the default) as the connection type:

void WorkerThread::receiveData(QVector<double> data)
{
    if(!b_abort_) // check if calculation is still running
    { 
        QEventLoop loop;
        connect(this, &WorkerThread::calcFinished, &loop, &QEventLoop::quit);
        b_abort_ = true; // set it to true and wait for calculation to stop
        loop.exec();
        // start new calculation
        RawData = data;
        startClac();
    }
    else
    {
        RawData = data;
        startClac();
    }
}

When I use this approach in my main GUI Thread, the generateImage() function blocks all event loops, and my GUI freezes, which makes me think that inside a single thread (main GUI thread or a worker thread) only one function can run at a time and so any change in b_abort_ is not applied until the thread's event loop returns to process other functions. When using WorkerThread it is difficult to verify if this is working, some times it works fine while other times it generates bad allocation error which seems like it is not working (although it might be because of a different reason entirely, I am not sure). I would like to ask your opinion, is this the right approach to stop a long-running calculation prematurely? Are there any other methods that I can use which will be more robust than my current approach?

Chaitanya
  • 177
  • 2
  • 9
  • Does this answer your question? [C++0x thread interruption](https://stackoverflow.com/questions/2790346/c0x-thread-interruption) – Ulrich Eckhardt Nov 05 '20 at 11:16
  • @UlrichEckhardt No, what I am looking for is to stop one long-running function from inside the same thread which called it, not thread stop/termination from a different thread. – Chaitanya Nov 05 '20 at 11:21
  • 1
    "inside a single thread [...] only one function can run at a time" -- this is the case. I'd rather say it differently: A thread executes code from a single place. Of course, that place can move between different functions but not be in two places at the same time. – Ulrich Eckhardt Nov 05 '20 at 11:25
  • @UlrichEckhardt I understand that, but how to know if the value of my variable `b_abort_` will be changed in between the `generateImage()` function is still running? – Chaitanya Nov 05 '20 at 11:31
  • I don't understand this question, did you mix up two sentences perhaps? – Ulrich Eckhardt Nov 05 '20 at 11:33
  • @UlrichEckhardt As you said "A thread executes code from a single place. Of course, that place can move between different functions but not be in two places at the same time." So, while the function `generateImage()` is running and I try to change the value of variable `b_abort_` in the same thread by invoking the slot in this thread from main GUI thread. Will the thread execute this slot to change `b_abort_` before it is done with `generateImage()` function or will it wait till the `generateImage()` function is finished and then execute the code inside the slot? – Chaitanya Nov 05 '20 at 11:38
  • A slot is just a way of calling a function. Your thread is already running a different function, so the callback function cannot run concurrently in the same thread. This follows immediately from the statement that a thread cannot "be in two places at the same time". – Useless Nov 05 '20 at 11:41
  • @UlrichEckhardt Ok, so basically I have to change the `b_abort_` variable from a different thread so that my `generateImage()` can be stopped prematurely? Is there no way I can do it from inside the same thread? – Chaitanya Nov 05 '20 at 11:43
  • 1
    That thread is **already doing something**, so not really. You could use a POSIX signal/handler, but then it is less portable (and the standard C++ interface isn't sufficient, since you need `pthread_kill` and/or `pthread_sigmask` to make this work). None of this will work automatically with Qt signals, so you'll still have some code executing in the main thread to send that signal. – Useless Nov 05 '20 at 12:08
  • 1
    The important part is not which thread sets it (but read again the answer below concerning "somehow synchronized") but that a thread that does something can not set it and vice versa. A thread can alternate between different things but it can not do two things at the same time. BTW: I'm not sure that's the misunderstanding, but if you have a C++ `QThread` or `std::thread` object somewhere, that is not a thread! It's more like a `std::fstream` is to a file, it is a C++ object that can be used to interact with the thread, just in case there lies your misunderstanding. – Ulrich Eckhardt Nov 05 '20 at 17:27

1 Answers1

3

How to stop a long-running function in another thread prematurely?

You're correct that the only sane way to do this is to have the long-running thread check, at regular intervals, whether it should stop early.

Note that the flag you're checking must be atomic, or protected by a mutex, or otherwise somehow synchronized. Otherwise it's entirely legitimate for the worker thread to check the variable and never see the value change (no, you can't use volatile for this).

... which makes me think that inside a single thread (main GUI thread or a worker thread) only one function can run at a time ...

Yes, that's exactly what a thread is! It is a single, linear thread of execution. It can't do two things at once. Doing two things at once is the whole reason for having more than one thread.

The approach should be to have a worker thread waiting for work to do, and a main thread that only ever sends it asynchronous messages (start generating an image with this data, or interrupt processing and start again with this data instead, or whatever).

If the main thread calls a function that should happen in the worker thread instead, well, you've deliberately started executing it in the main thread, and the main thread won't do anything until it returns. Just like every other function.


As an aside, your design has a problem: it's possible to never finish generating a single image if it keeps being interrupted by new data.

The usual solution is double-buffering: you let the worker thread finish generating the current image while the main thread accumulates data for the next one. When the worker has finished one image, it can be passed back to the main thread for display. Then the worker can start processing the next, so it takes the buffer of "dirty" updates that the main thread has prepared for it. Subsequent updates are again added to the (now empty) buffer for the next image.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Ok, I understand so use main thread to check and change the value of `b_abort_` and then the `generateImage()` function inside worker thread will be able to return prematurely, right? Regarding double buffering, it is not so useful for me as most of the time when I have to stop and recalculate is when the user is interacting with the plot, such as scrolling or zooming which generates events constantly and so I need to stop my calculation prematurely in my worker thread. – Chaitanya Nov 05 '20 at 11:45
  • I would like my worker thread to be as much self-sufficient as possible. Is there absolutely no way I can achieve the same premature return of a function from inside the same thread in which the function is running? – Chaitanya Nov 05 '20 at 11:49
  • 1
    You're looking at implementing co-operative multitasking or co-routines within a single thread in a multi-threaded app. It's complex and doesn't really seem worthwhile. You could have the worker object own _two_ threads - a long-running compute thread, and an idle watchdog thread to which you can send your abort request - but this doesn't really seem better than just having the main thread set the abort flag itself. – Useless Nov 05 '20 at 11:59