I'm doing my first steps in QtConcurrent. I try to implement an iterating calculation and in order to avoid copying large amount of data (in this example it's reduced for demonstration purpose) I implement something similar like a double buffer in a shared data object (derived from QSharedData
) and accessed via QSharedDataPointer
.
qtconcurrent_example.h
#ifndef QTCONCURRENT_EXAMPLE_H_INCLUDED
#define QTCONCURRENT_EXAMPLE_H_INCLUDED
#include <functional>
#include <QList>
#include <QDebug>
#include <QSharedData>
typedef QVector<double> StateType;
class StateData : public QSharedData
{
StateType state_left;
StateType state_right;
public:
StateType* state_cur;
StateType* state_new;
public:
StateData(StateType initState){
this->state_left = initState;
this->state_right = StateType();
this->state_right.resize(initState.length());
this->state_cur = &(this->state_left);
this->state_new = &(this->state_right);
}
void swap(){
qInfo() << "## swap start ##"<< " cur:"<< QString::pointer(this->state_cur)
<< " new:"<< QString::pointer(this->state_new);
StateType* cur = this->state_cur;
this->state_cur = this->state_new;
this->state_new = cur;
qInfo() << "## swap end ##"<< " cur:"<< QString::pointer(this->state_cur)
<< " new:"<< QString::pointer(this->state_new);
}
};
typedef QSharedDataPointer<StateData> State;
typedef std::function<double(const StateType&)> Odefun;
class WorkItem
{
public:
WorkItem(int index, State data, Odefun odefun){
this->index = index;
this->data = data;
this->odefun = odefun;
}
int index;
State data;
Odefun odefun;
};
class QtConcurrent_example
{
// shared data pointer to the state data
State state;
public:
QtConcurrent_example();
};
#endif // QTCONCURRENT_EXAMPLE_H_INCLUDED
qtconcurrent_example.cpp
#include "qtconcurrent_example.h"
#include <QtConcurrent>
QtConcurrent_example::QtConcurrent_example()
{
// initialize state data and link the objet to the shared
// data pointer
StateType init = {1,2,3};
state = new StateData(init);
qInfo() << "\t"<< QString::number(state.data()->state_cur->at(0))
<< "\t"<< QString::number(state.data()->state_cur->at(1))
<< "\t"<< QString::number(state.data()->state_cur->at(2))
<< "\tp:"<< QString::pointer(state.data()->state_cur);
QList<WorkItem> workSet;
workSet.append( WorkItem( 0, state,
[](const StateType &s){
return s.at(0)*2;
} ));
workSet.append( WorkItem( 1, state,
[](const StateType &s){
return s.at(1)*2;
} ));
workSet.append( WorkItem( 2, state,
[](const StateType &s){
return s.at(2)*2;
} ));
std::function<void(const WorkItem &)> step =
[](const WorkItem &wi){
qInfo() << "## step ##"<< " cur:"<< QString::pointer(wi.data.data()->state_cur)
<< " new:"<< QString::pointer(wi.data.data()->state_new);
// apply ode at index
auto r = wi.odefun(*wi.data.data()->state_cur);
// apply integration
r = wi.data.data()->state_cur->at(wi.index) + r*0.01;
// store data
wi.data.data()->state_new->replace(wi.index, r);
};
StateType* cur;
for(int i=0; i<10; i++){
cur = state.data()->state_cur;
qInfo() << "apply blockingMap step"<< "\tp:"<< QString::pointer(cur);
QtConcurrent::blockingMap(workSet, step);
cur = state.data()->state_cur;
qInfo() << "apply swap"<< "\tp:"<< QString::pointer(cur);
state.data()->swap();
cur = state.data()->state_cur;
qInfo() << "> i="<< QString::number(i)
<< "\t"<< QString::number(cur->at(0))
<< "\t"<< QString::number(cur->at(1))
<< "\t"<< QString::number(cur->at(2))
<< "\tp:"<< QString::pointer(cur);
}
}
The corresponding output is:
"1""2""3" p: 0x55c3f5d06a08
## swap start ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
## swap end ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
apply blockingMap step p: 0x55c3f5d06a10
## step ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
## step ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
## step ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
apply swap p: 0x55c3f5d06a10
## swap start ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
## swap end ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
> i= "0""1""0""0" p: 0x55c3f5d06a08
apply blockingMap step p: 0x55c3f5d06a08
## step ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
## step ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
## step ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
apply swap p: 0x55c3f5d06a08
## swap start ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
## swap end ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
> i= "1""1.02""0""0" p: 0x55c3f5d06a10
apply blockingMap step p: 0x55c3f5d06a10
## step ## cur: 0x55c3f5d06a08 new: 0x55c3f5d06a10
## step ## cur: 0x55c3f5d06a10 new: 0x55c3f5d06a08
...
And I do not get it, why the pointer state_cur
is always the same inside QtConcurrent::blockingMap(workSet, step)
even if it change outside (due to swap
).
Why?
It looks to me as the pointer will fixed while
workSet.append( WorkItem( 0, state,
[](const StateType &s){
return s.at(0)*2;
} ));
Because: if I call
state.data()->swap();
bevore, between or behind the three workSet.append(...)
calls, it does effect the pointer inside the corresponding call, although the pointer state_cur
does not change anymore.
Removing the const
's does not help - as expected.