سؤال

The QtConcurrent namespace is really great for simplifying the management of multi-threaded calculations. Overall this works great and I have been able to use QtConcurrent run(), map(), and other variants in the way they are described in the API.

Overall Goal:

I would like to query, cancel(), or pause() a numerically intensive calculation from QML. So far this is working the way I would like, except that I cannot access the sequence numbers in the calculation. Here is a link that describes a similar QML setup.

Below is an image from small test app that I created to encapsulate what I am trying to do:

enter image description here

In the example above the calculation has nearly completed and all the cores have been enqueued with work properly, as can be seen from a system query:

enter image description here

But what I really would like to do is use the sequence numbers from a given list of the items IN THE multi-threaded calculation itself. E.g., one approach might be to simply setup the sequence numbers directly in a QList or QVector (other C++ STL containers can work as well), like this:

void TaskDialog::mapTask()
{
    // Number of times the map function will be called:
    int N = 5;

    // Prepare the vector that we operate on with mapFunction:
    QList<int> vectorOfInts;
    for (int i = 0; i < N; i++) {
        vectorOfInts << i;
    }

    // Start the calc:
    QFuture<void> future = QtConcurrent::map(vectorOfInts, mapFunction);
    _futureWatcher.setFuture(future);
    //_futureWatcher.waitForFinished();
}

The calculation is non-blocking with the line: _futureWatcher.waitForFinished(); commented out, as shown in the code above. Note that when setup as a non-blocking calculation, the GUI thread is responsive, and the progress bar updates as desired.

But when the values in the QList container are queried during the calculation, what appears seem to be the uninitialized garbage values that one would expect when the array is not properly initialized.

Below is the example function I am calling:

void mapFunction(int& n)
{
    // Check the n values:
    qDebug() << "n = " << n;
    /* Below is an arbitrary task but note that we left out n,
     * although normally we would want to use it): */
    const long work = 10000 * 10000 * 10;
    long s = 0;
    for (long j = 0; j < work; j++)
        s++;
}

And the output of qDebug() is:

n =  30458288 
n =  204778 
n =  270195923 
n =  0 
n =  270385260 

The n-values are useless but the sum values, s, are correct (although not shown) when the calculation is mapped in this fashion (non-blocking).

Now, if I uncomment the _futureWatcher.waitForFinished(); line then I get the expected values (the order is irrelevant):

n =  0 
n =  2 
n =  4 
n =  3 
n =  1

But in this case, with _futureWatcher.waitForFinished(); enabled, my GUI thread is blocked and the progress bar does not update.

What then would be the advantage of using QtConcurrent::map() with blocking enabled, if the goal to not block the main GUI thread?

Secondly, how can get the correct values of n in the non-blocking case, allowing the GUI to remain responsive and have the progress bar keep updating?

My only option may be to use QThread directly but I wanted to take advantage of all the nice tools setup for us in QtConcurrent.

Thoughts? Suggestions? Other options? Thanks.


EDIT: Thanks to user2025983 for the insight which helped me to solve this. The bottom line is that I first needed to dynamically allocate the QList:

QList<int>* vectorOfInts = new QList<int>;
for (int i = 0; i < N; i++)
    vectorOfInts->push_back(i);

Next, the vectorOfInts is passed by reference to the map function by de-referencing the pointer, like this:

QFuture<void> future = QtConcurrent::map(*vectorOfInts, mapFunction);

Note also that the prototype of the mapFunction remains the same:

void mapFunction(int& n)

And then it all works properly: the GUI remained responsive, progress bar updated, the values of n are all correct, etc., WITHOUT the need to add blocking through the function:

_futureWatcher.waitForFinished();

Hope these extra details can help someone else.

هل كانت مفيدة؟

المحلول

The problem here is that your QList goes out of the scope when mapTask() finishes. Since the mapFunction(int &n) takes the parameter by reference, it gets references to integer values which are now part of an array which is out of scope! So then the computer is free to do whatever it likes with that memory, which is why you see garbage values. If you are just using integer parameters, I would recommend passing the parameters by value and then everything should work.

Alternatively, if you must pass by reference you can have the futureWatcher delete the array when its finished.

    QList<int>* vectorOfInts = new QList<int>;
    // push back into structure
    connect(_futureWatcher, SIGNAL(finished()), vectorOfInts, SLOT(deleteLater()));
    // launch stuff
    QtConcurrent::map...
    // profit
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top