Share object between two lambdas - c++

I am trying to share the same object (in my case, a string) between to lambdas using shared_ptr:
auto fileToLoad = make_shared<string>();
StartAsync(
[=]()
{
QDir dir(dirPath.c_str());
QString fileExtension(fileExt.c_str());
for (int i = 0; i < 150; i++)
{
auto files = dir.entryList(QStringList() << fileExtension << fileExtension.toUpper(), QDir::Files);
if (!files.empty())
{
//fileToLoad.reset(new string(files.first().toUtf8().constData()));
fileToLoad = make_shared<string>(files.first().toUtf8().constData());
break;
}
this_thread::sleep_for(chrono::milliseconds(200));
}
},
[=]()
{
if (fileToLoad == nullptr)
{
DisplayMessage("Timeout reached");
}
else
{
doFunc(*fileToLoad);
}
});
After many attempts I have still not been able to make it work as I want: to store fileToLoad in the first lambda and use it in the second. Or it fails to compile due to qualifiers (const), or it compiles but 'fileToLoad' remains empty.
As you can see I am trying to notify if a file has appeared in a folder, using StartAsync function, which takes 2 lambdas (this function basically creates QObjects and move them to background thread and then makes some connections between signals and slots).
Edit:
StartAsync: takes a lambda of a task (lengthy work), and a lambda of post task (UI updates)
void MainWidget::StartAsync(function<void()> func, function<void()> postTask)
{
AsyncTask* task = new AsyncTask(func, [=]() { if (postTask != nullptr) { postTask(); HideProgressBar(); }});
QThread* thread = new QThread();
task->moveToThread(thread);
connect(thread, &QThread::started, this, &MainWidget::ShowProgressBar);
connect(thread, &QThread::started, task, &AsyncTask::RunTask);
connect(task, &AsyncTask::TaskFinished, task, &AsyncTask::RunPostTask);
connect(thread, &QThread::finished, task, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
}
Any suggestions are appreciated.

When you assign a new value to a std::shared_ptr - you create a new shared value, that doesn't have any connection with the previous one (you can have a look at counters of both shared_ptrs). You should change
fileToLoad = make_shared<string>(files.first().toUtf8().constData());
to
*fileToLoad = files.first().toUtf8().constData()
(of course checking beforehand that fileToLoad isn't null)

You code won't work, because in first lambda you overwrite the temporary pointer inside lambda. What you can do is overwrite std::string behind the pointer and it should work fine:
auto fileToLoad = make_shared<string>();
StartAsync(
[=]()
{
QDir dir(dirPath.c_str());
QString fileExtension(fileExt.c_str());
for (int i = 0; i < 150; i++)
{
auto files = dir.entryList(QStringList() << fileExtension << fileExtension.toUpper(), QDir::Files);
if (!files.empty())
{
//fileToLoad.reset(new string(files.first().toUtf8().constData()));
*fileToLoad = files.first().toStdString();
break;
}
this_thread::sleep_for(chrono::milliseconds(200));
}
},
[=]()
{
if (fileToLoad == nullptr)
{
DisplayMessage("Timeout reached");
}
else
{
doFunc(*fileToLoad);
}
});

Related

How to replace Qthread with QtConcurrent

I have a SQL fetching class that connects to SQL and fetched the required data.
I wanted to this in another thread so I did that with QThread.
It is working as it should be.
But now I want to replace it with QTConcurrent. The problem I face with QTconcureent is that I need a connect command that initializes the database before the thread uses to do SQL query.
Here are my codes created is the public slot and qint64TotalSize is the public method of the SqlFetcher class.
Controller::Controller(QObject *parent) : QObject(parent)
{
SqlFetcher* m_Fetcher = new SqlFetcher();
qInfo() <<"Start"<< QThread::currentThread();
QFutureWatcher<void> watcher;
QFuture <void> future1 = QtConcurrent::run(m_Fetcher,&SqlFetcher::qint64TotalSize);
watcher.setFuture(future1);
//QThread* t1 = new QThread();
//m_Fetcher->moveToThread(t1);
//connect(t1, &QThread::started, m_Fetcher, &SqlFetcher::createDb);
//connect(t1, &QThread::started, m_Fetcher, &SqlFetcher::qint64TotalSize);
//t1->start();
qInfo() <<"Finish"<< QThread::currentThread();
}
void SqlFetcher::qint64TotalSize()
{
qint64 l_result= 0;
QSqlQuery l_query;
if (m_sqldb.isValid())
{
m_sqldb.open();
if ((m_sqldb.isOpen()))
{
l_query.prepare("SELECT COUNT(*) FROM table1");
l_query.exec();
if (l_query.next()) {
l_result= l_query.value(0).toInt();
}
m_sqldb.close();
}
}
qInfo() << l_result << QThread::currentThread();
}
void SqlFetcher::createDb()
{
m_sqldb = QSqlDatabase::addDatabase("QSQLITE");
m_sqldb.setDatabaseName("xyz.db");
qInfo() << "createDB" << QThread::currentThread();
}
My current output is
Start QThread(0x7feab4c0f060)
Finish QThread(0x7feab4c0f060)
0 QThread(0x7feab4d42070, name = "Thread (pooled)")
Expected Output or Output with Qthread is
Start QThread(0x7fe82140f060)
Finish QThread(0x7fe82140f060)
createDB QThread(0x7fe82155c840)
151 QThread(0x7fe82155c840)
Try executing the whole task in a run, e.g.
QtConcurrent::run([](){
SqlFetcher m_Fetcher;
m_Fetcher.createDb();
m_Fetcher.qint64TotalSize();
});
Since we're dealing with concurrency, it's safer to use named connections (otherwise the same default connection will be used every time, possibly by more than one thread). You can manage this by adding an argument to SqlFetcher::createDb:
void SqlFetcher::createDb(const QString & connectionName)
{
m_sqldb = QSqlDatabase::addDatabase("QSQLITE", connectionName);
// etc.
and to the lambda and the run function, as well:
QtConcurrent::run([](const QString & cn){
SqlFetcher m_Fetcher;
m_Fetcher.createDb(cn);
m_Fetcher.qint64TotalSize();
}, QString("TheConnectionName"));
In the other function, assign the database to the query, in construction:
void SqlFetcher::qint64TotalSize()
{
qint64 l_result= 0;
QSqlQuery l_query(m_sqldb);
//etc.

Qt Multiple Async While Loops and qApp->processEvents();

I am trying to do a project is creating some graphics on window with Qt GUI C++ 5.6.2.
I have two methods named 'createVerticalSpeedIndicator' and 'createAirSpeedIndicator'. These methods need to create some graphics with a while(1) loop and use qApp->processEvents(); on window and they are doing it perfectly when one of them is working the other one is deactive. But I need to run both of them simultanously and always.
What can I do to run them simultanously and always.
Thank you So much
The solution is to invert the control flow. The while() { ... processEvents() ... } is an anti-pattern in asynchronous code, because it assumes that you have locus of control whereas you really don't. You're lucky that you didn't run out of stack since processEvents could potentially re-enter the createXxx methods.
Here's a complete example of a transformation:
// Input
void Class::createVerticalSpeedIndicator() {
for (int i = 0; i < 100; i ++) {
doStep(i);
QCoreApplication::processEvents();
}
}
// Step 1 - factor out state
void Class::createVerticalSpeedIndicator() {
int i = 0;
while (i < 100) {
doStep(i);
QCoreApplication::processEvents();
i++;
}
};
// Step 2 - convert to continuation form
void Class::createVerticalSpeedIndicator() {
int i = 0;
auto continuation = [=]() mutable {
if (!(i < 100))
return false;
doStep(i);
QCoreApplication::processEvents();
i++;
return true;
};
while (continuation());
};
// Step 3 - execute the continuation asynchronously
auto Class::createVerticalSpeedIndicator() {
int i = 0;
return async(this, [=]() mutable {
if (!(i < 100))
return false;
doStep(i);
i++; // note the removal of processEvents here
return true;
});
};
template <typename F> void async(QObject * parent, F && continuation) {
auto timer = new QTimer(parent);
timer->start(0);
connect(timer, &QTimer::timeout, [timer, c = std::move(continuation)]{
if (!c())
timer->deleteLater();
});
}
At that point you can apply the same transformation to createAirSpeedIndicator and start them in the constructor of your class:
Class::Class(QWidget * parent) : QWidget(parent) {
...
createVerticalSpeedIndicator();
createAirSpeedIndicator();
}
Both tasks will run asynchronously and pseudo-concurrently within the main thread, i.e. each task will alternatively execute a single step.
Suppose we wanted to chain the tasks, i.e. have a task start only after the previous one has finished. The modification in the user code can be simple:
Class::Class(QWidget * parent) : QWidget(parent) {
...
createVerticalSpeedIndicator()
>> createAirSpeedIndicator()
>> someOtherTask();
}
The async function must now return a class that allows such connections to be made:
struct TaskTimer {
QTimer * timer;
TaskTimer & operator>>(const TaskTimer & next) {
next.timer->stop();
connect(timer, &QObject::destroyed, next.timer, [timer = next.timer]{
timer->start(0);
});
timer = next.timer;
return *this;
}
};
template <typename F> TaskTimer async(QObject * parent, F && continuation) {
TaskTimer task{new QTimer(parent)};
task.timer->start(0);
connect(task.timer, &QTimer::timeout,
[timer = task.timer, c = std::move(continuation)]{
if (!c())
timer->deleteLater();
});
return task;
}

tbb concurrent_bounded_queue multiple threads access

I have the following code:
tbb::concurrent_bounded_queue<Image> camera_queue_;
camera_queue_.set_capacity(1);
struct Image
{
int hour_;
int minute_;
int second_;
int msec_;
QImage image_;
Image(){hour_ = -1; minute_ = -1; second_ = -1; msec_ = -1; image_ = QImage();}
Image& operator=(Image const& copy)
{
this->hour_ = copy.hour_;
this->minute_ = copy.minute_;
this->second_ = copy.second_;
this->msec_ = copy.msec_;
this->image_ = copy.image_;
return *this;
}
};
In a Qt Thread :
ThreadA:
tbb::concurrent_bounded_queue<Image> image_queue_;
image_queue_.set_capacity(1);
Image cur_image_;
void Worker::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.pop(cur_image_);
image_queue_.push(cur_image_);
}
emit imageReady();
}
emit finished();
}
Image Worker::getCurrentImage()
{
Image tmp_image;
image_queue_.pop(tmp_image);
return tmp_image;
}
In Another Thread:
ThreadB:
Producer::Producer(){
work_ = new Worker();
work_->moveToThread(workerThread_);
QObject::connect(workerThread_, &QThread::finished, work_, &QObject::deleteLater);
QObject::connect(this, &Producer::operate, work_, &Worker::process);
QObject::connect(work_, &Worker::imageReady, this, &Producer::displayImage);
QObject::connect(this, &Producer::stopDecode, work_, &Worker::stop);
workerThread_->start();
emit operate();
}
void Producer::process() {
while(1)
{
if(quit_)
break;
{
camera_queue_.push(GetImage());
}
}
}
void Producer::displayImage()
{
Image tmp = std::move(work_->getCurrentImage());
widget_->showImage(tmp.image_);
}
However, In main thread, I have a function that enables user to click a button to get current image:
bool Producer::SaveImage()
{
Image img = std::move(work_->getCurrentImage());
std::string fileName = std::to_string(img.hour_) + "-" + std::to_string(img.minute_) + "-" + std::to_string(img.second_) + "-" + std::to_string(img.msec_/1000) + ".jpg";
std::string outFileName = folder + "/" + fileName;
return img.image_.save(QString::fromStdString(outFileName));
}
The problem is:
When user does not click the button to invoke Producer::SaveImage(), the Image Decoding and Showing runs smoothly. But when user invoke Producer::SaveImage(), the whole program will get stuck (Caton phenomenon ?). The GUI response becomes not that smooth. The more user invokes SaveImage, the slower the GUI response becomes.
Can anyone help to explain why ? Is there a way to solve that ?
Why do you want to use concurrent queue? It looks like there is a syncronization mechanism in place and you rely mostly on it instead of using concurrent_queue for synchronisation and communication as it is supposed for.
The issue is that when you set capacity = 1, both operations of concurrent_bounded_queue will block until there is enough space of items in the queue. E.g. if the queue contains an item already, the push operation will block. And since you control your threads with another notification mechanism, you might catch a deadlock.
In particular, try to swap the operations like below:
camera_queue_.pop(cur_image_);
emit imageReady();
image_queue_.push(cur_image_);
This should prepare the thread which receives images (if I understand this correctly) and it will block on its image_queue_.pop() method, then this thread will put new image and unblocks the recipient. There might be other issues similar to this, so, please rethink all of your synchronization.

Best way to design multple and nested GET/POST in QT with QNetworkManager

the doubt i have is about the correct design of a software which implements multiple and nested GET/POST request.
Say you have to run a login() function which requires a GET and a POST and then retrieveXYZ() which requires two GETs (and so on, scalable).
The way i was thinkig to do it was like
mainwindow.cpp
//code
login();
retrieveXYZ();
//code
Mainwindow::login(){
//code
connect(nam, SIGNAL(finished()), this, SLOT(onGetLoginFinished()));
nam->get(...);
}
Mainwindow::onGetLoginFinished(){
//do stuff
connect(nam, SIGNAL(finished()), this, SLOT(onPostLoginFinished()));
nam->post(...);
}
Mainwindow::onPostLoginFinished(){
//do stuff
}
Mainwindow::retrieveXYZ(){
//code
connect(nam, SIGNAL(finished()), this, SLOT(onGet1RetrieveFinished()));
nam->get();
//code
}
Mainwindow::onGet1RetrieveXYZFinished(){
//do stuff
connect(nam, SIGNAL(finished()), this, SLOT(onGet2RetrieveFinished()));
nam->get();
}
or should i use something like QSignalMapper ?
Which are the most correct/efficient way to do so? i've seen people using sender() cast but i didn't understand the point.
Basically i would like to retrieve the particular reply finished() signal rather than the general one (or of the qnam)
This method may work but it is not nice and clean to me
Is this the best we can get?
http://www.johanpaul.com/blog/2011/07/why-qnetworkaccessmanager-should-not-have-the-finishedqnetworkreply-signal/
Moving the connect approach to the reply?
I got something like:
struct RequestResult {
int httpCode;
QByteArray content;
};
RequestResult
ExecuteRequest(const std::function<QNetworkReply*(QNetworkAccessManager&)>& action,
const std::chrono::milliseconds& timeOut)
{
QEventLoop eLoop;
QTimer timeOutTimer;
QNetworkAccessManager nam;
QObject::connect(&timeOutTimer, &QTimer::timeout, &eLoop, &QEventLoop::quit);
QObject::connect(&nam, &QNetworkAccessManager::finished, &eLoop, &QEventLoop::quit);
timeOutTimer.setSingleShot(true);
timeOutTimer.setInterval(timeOut.count());
timeOutTimer.start();
auto resetTimeOut = [&timeOutTimer]() { timeOutTimer.start(); };
QNetworkReply* reply = action(nam);
QObject::connect(reply, &QNetworkReply::uploadProgress, resetTimeOut);
QObject::connect(reply, &QNetworkReply::downloadProgress, resetTimeOut);
eLoop.exec();
if (!timeOutTimer.isActive())
{
throw std::runtime_error("Time out"); // Probably custom exception
}
const int httpStatus
= reply->attribute(QNetworkRequest::Attribute::HttpStatusCodeAttribute).toInt();
auto content = TakeContent(*reply); // reply->readAll and decompression
return RequestResult{httpStatus, content};
}
And then functions for get/delete/post/.. which are similar to
auto RequestGet(const QNetworkRequest& request) {
return ExecuteRequest([&](QNetworkAccessManager& nam) { return nam.get(request); },
timeOut);
}
auto RequestDelete(const QNetworkRequest& request) {
return ExecuteRequest([&](QNetworkAccessManager& nam) {
return nam.deleteResource(request);
},
timeOut);
}
auto RequestPost(const QNetworkRequest& request, QHttpMultiPart& multiPart)
{
return ExecuteRequest([&](QNetworkAccessManager& nam) {
return nam.post(request, &multiPart);
},
timeOut);
}
Then, for your code, I would do something like
Mainwindow::login()
{
const auto getRes = RequestGet(..);
// ...
const auto postRes = RequestPost(..);
// ...
}
And you may use thread and future if you want not blocking calls.

Qt - resolve two sequential calls on a slot and perform action only once

I have a CodeEditor class which inherits from QPlainTextEdit. This class has to emit requests when textCursor()'s position or selection changes. The thing is, I don't want 2 calls to assembleIntellisenseRequest and pickListSortRequest signals when both cursorPositionChanged and selectionChanged is emitted. I solved this with adding bool update_ member to CodeEditor, which is set to true in constructor. 500ms delay is just for clarity.
void CodeEditor::makeConnections()
{
auto updateRequest = [this]()
{
if(update_ && !textCursor().hasSelection())
{
update_ = false;
QTimer::singleShot(500, [this]() { update_ = true; });
emit assembleIntellisenseRequest(textCursor().blockNumber());
emit pickListSortRequest(textCursor().positionInBlock());
}
};
connect(this, &CodeEditor::cursorPositionChanged, updateRequest);
connect(this, &CodeEditor::selectionChanged, updateRequest);
}
Is there any better way to accomplish this? Also, why was in this case, when lambda captures by reference this1: printout not equal to this:? It was silent, I just knew, that update_ is still false.
auto updateRequest = [this]()
{
cout << "this: " << this << endl;
if(update_ && !textCursor().hasSelection())
{
update_ = false;
QTimer::singleShot(500, [&]()
{
cout << "this1: " << this << endl;
update_ = true;
});
emit assembleIntellisenseRequest(textCursor().blockNumber());
emit pickListSortRequest(textCursor().positionInBlock());
}
};
Thank you in advance.
You can apply next pattern to your code:
class MyClass : public QObject
{
private slots:
void updateRequest();
private:
QTimer *_timer;
CodeEditor *_editor;
};
MyClass::MyClass()
{
// Init members
// ...
_timer->setSingleShot( true );
_timer->setInterval( 0 );
connect( _editor, &CodeEditor:: cursorPositionChanged, _timer, &QTimer::start);
connect( _editor, &CodeEditor:: selectionChanged, _timer, &QTimer::start);
connect( _timer, &QTimer::timeout, this, &MyClass::updateRequest );
}
In this solution, timer is a "proxy for signals". Each time signal is emited timer will start immedeately (when flow will return to an event loop). Each emitting of signal will call QTimer::start slot. But all calls of start will place only one call of timeout signal to event queue. So, when control flow will return to event loop, your updateRequest slot will be called only once, even if a lot of signals were emited.
QTimer is a "Qt way" to replace your update_ variable, without any timeouts.