I want to make my application multithreaded. When I added 2 separate independent threads I have got runtime error message. I can't find the solution. Perhaps someone may help.
Here is link to runtime error image https://postimg.org/image/aasqn2y7b/
threads.h
#include <thread>
#include <atomic>
#include <chrono>
#include <iostream>
class Threads
{
public:
Threads() : m_threadOne(), m_threadTwo(), m_stopper(false) { }
~Threads() {
m_stopper.exchange(true);
if (m_threadOne.joinable()) m_threadOne.join();
if (m_threadTwo.joinable()) m_threadTwo.join();
}
void startThreadOne() {
m_threadOne = std::thread([this]() {
while (true) {
if (m_stopper.load()) break;
std::cout << "Thread 1" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
void startThreadTwo() {
m_threadOne = std::thread([this]() {
while (true) {
if (m_stopper.load()) break;
std::cout << "Thread 2" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
});
}
private:
std::thread m_threadOne;
std::thread m_threadTwo;
std::atomic<bool> m_stopper;
};
mainwindow.h
#include "threads.h"
#include <QMainWindow>
#include "ui_mainwindow.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0) : QMainWindow(parent), ui(new Ui::MainWindow), m_threads() {
ui->setupUi(this);
m_threads.startThreadOne();
m_threads.startThreadTwo();
}
~MainWindow() { delete ui; }
private:
Ui::MainWindow *ui;
Threads m_threads;
};
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Your start thread two is broken:
m_threadOne = std::thread([this]() { ... });
After starting thread one, m_thread_one gets another thread assigned. However, the thread one is not joined, hence the termination.
Related
I have been facing issues with a simple C++ group project I am working on. I am trying to create a thread that does a background job while my UI and the main app is working perfectly fine. I tried to the same thing on a test file before and it worked fine, however doing it in the Qt Project doesn't works somehow. I do not have much idea about Qt for ref. Here's my code. I am trying to update the progress bar repeatedly till my app runs.
Here's my code for more over view
test code
#include <thread>
#include <iostream>
#include <windows.h>
int getRamUsage() {
MEMORYSTATUSEX statex;
statex.dwLength = sizeof (statex);
GlobalMemoryStatusEx (&statex);
return statex.dwMemoryLoad;
}
void ramUsage() {
while (true) {
std::cout << "RAM usage: " << getRamUsage() << "%" << std::endl;
Sleep(1000);
}
}
int main() {
auto ramLoop = [=]() {
while (true) {
std::cout << "RAM usage: " << getRamUsage() << "%" << std::endl;
Sleep(1000);
}
};
std::thread t1(ramLoop);
t1.join();
}
It works, But it doesn't in the Front Ended Qt one I mentioned. May I know any other ideas to make this work.
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "ramUsage.h"
#include <thread>
#include <windows.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
auto ramLoop = [=]() {
while (true) {
ui->ramBar->setValue(getRamUsage());
Sleep(1000);
}
};
std::thread t(ramLoop);
t.join();
}
MainWindow::~MainWindow()
{
delete ui;
}
main.cpp
#include "mainwindow.h"
#include <stdio.h>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
ramUsage.cpp
#include <windows.h>
#include "../ramUsage.h"
int getRamUsage() {
MEMORYSTATUSEX statex;
statex.dwLength = sizeof (statex);
GlobalMemoryStatusEx (&statex);
return statex.dwMemoryLoad;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
ramUsage.h
#pragma once
#ifndef RAMUSAGE_H
#define RAMUSAGE_H
int getRamUsage();
#endif // RAMUSAGE_H
I tried using QProcess and the basic threading options for windows which doesn't seem to work
//Work.h
#ifndef WORK_H
#define WORK_H
#include <QDebug>
#include <QObject>
#include <QThread>
class Work : public QObject {
Q_OBJECT
public:
explicit Work(QObject *parent = nullptr);
public slots:
void snap();
void setStatus();
signals:
private:
bool status;
};
#endif // WORK_H
//Work.cpp
#include "Work.h"
Work::Work(QObject *parent) : QObject(parent) { status = true; }
void Work::snap() {
status = true;
while (true) {
if (status) {
qDebug() << "Work thread: " << QThread::currentThreadId();
} else {
qDebug() << "STOP";
break;
}
}
}
void Work::setStatus() { status = false; }
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
#include "Work.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
Work *work;
QThread thread;
};
#endif // MAINWINDOW_H
//MainWindow.cpp
#include "MainWindow.h"
#include "ui_MainWindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
work = new Work();
work->moveToThread(&thread);
thread.start();
connect(ui->startButton, SIGNAL(clicked()), work, SLOT(snap()));
connect(ui->stopButton, SIGNAL(clicked()), work, SLOT(setStatus()));
}
MainWindow::~MainWindow() {
thread.terminate();
delete ui;
}
//main.cpp
#include <QApplication>
#include "MainWindow.h"
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MainWindow w;
qDebug() << QThread::currentThreadId();
w.show();
return a.exec();
}
I use MainWindow to display, Work to do something. And I use work->moveToThread(&thread).
Click start button to execute snap function in Work, what I want to do is when I click stop button, the snap function output STOP. And I can still start and stop whenever I like.
But I fail. It seems impossible to change the status during the while loop. Work doesn't get the stopButton clicked signal. Is it because of priority?
Could anyone give me some advices?
Firstly consider your Work::snap implementation...
void Work::snap() {
status = true;
while (true) {
if (status) {
qDebug() << "Work thread: " << QThread::currentThreadId();
} else {
qDebug() << "STOP";
break;
}
}
}
Once started it never yields control to the Qt event loop. Now consider the connect call...
connect(ui->stopButton, SIGNAL(clicked()), work, SLOT(setStatus()));
Since ui->stopButton and work have different thread affinities this is effectively a queued connection and requires the receiver to have an active event loop. Hence the call to setStatus will remain pending forever.
A better way to achieve your goal might be to make use of a simple atomic bool...
std::atomic<bool> status;
and change the connect call to modify status directly using a lambda (untested)...
connect(ui->stopButton, &QPushButton::clicked, [this]{ work->setStatus(); });
I solve it.
I add a slot and a signal in MainWindow and change stop slot.
connect(ui->startButton, &QPushButton::clicked, this, &MainWindow::start);
connect(this, &MainWindow::startSnap, work, &Work::snap);
// start slot
void MainWindow::start() {
thread.start();
emit startSnap();
}
void MainWindow::stop() {
if (thread.isRunning()) {
thread.requestInterruption();
}
}
And change the codes in Work::snap
void Work::snap() {
while (true) {
if (QThread::currentThread()->isInterruptionRequested()) {
qDebug() << "STOP";
QThread::currentThread()->exit();
return;
} else {
qDebug() << "Work thread: " << QThread::currentThreadId();
}
}
}
The key codes are:
thread.requestInterruption();(MainWindow::stop)
QThread::currentThread()->exit();(Work::snap)
thread.start();(MainWindow::start)
I'm running some tests in order to undestand what is the best way to update the GUI of a QDialog in a separate thread.
I did the following:
main.cpp (untouched)
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
dialog.h: the SLOT draw_point(QPointF) is connected to the signal emitted by the worker class, in order to update the scene; int he UI there are only two buttons, a Start button that obviously starts the computation, and a Stop button that interrupts the computation.
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QElapsedTimer>
#include <QGraphicsScene>
#include <QThread>
#include "worker.h"
namespace Ui {
class Dialog;
}
class Dialog : public QDialog
{
Q_OBJECT
public:
explicit Dialog(QWidget *parent = 0);
~Dialog();
private:
Ui::Dialog *ui;
QGraphicsScene scene;
QThread t;
QElapsedTimer chronometer;
worker work;
public slots:
void draw_point(QPointF p);
private slots:
// for Start button
void on_pushButton_clicked();
// for Stop button
void on_pushButton_2_clicked();
void print_finished();
};
#endif // DIALOG_H
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QTimer>
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
qDebug() << "Starting dialog thread" << thread();
ui->setupUi(this);
scene.setSceneRect(0, 0, 400, 400);
ui->graphicsView->setScene(&scene);
ui->graphicsView->setFixedSize(400, 400);
work.moveToThread(&t);
connect(&work, SIGNAL(new_point(QPointF)), this, SLOT(draw_point(QPointF)));
connect(&t, SIGNAL(started()), &work, SLOT(doWork()));
connect(&work, SIGNAL(finished()), &t, SLOT(quit()));
connect(&work, SIGNAL(finished()), this, SLOT(print_finished()));
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::draw_point(QPointF p)
{
scene.addEllipse(p.x(), p.y(), 1.0, 1.0);
}
// Start button
void Dialog::on_pushButton_clicked()
{
t.start();
chronometer.start();
}
// Stop button
void Dialog::on_pushButton_2_clicked()
{
work.running = false;
}
void Dialog::print_finished()
{
qDebug() << "Finished dialog thread" << thread();
qDebug() << "after" << chronometer.elapsed();
}
worker.h
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include <QPointF>
#include <QVector>
class worker : public QObject
{
Q_OBJECT
public:
explicit worker();
bool running;
signals:
void new_point(QPointF);
void finished();
public slots:
void doWork();
};
#endif // WORKER_H
worker.cpp
#include "worker.h"
#include <QDebug>
#include <QElapsedTimer>
#include <QPoint>
#include <QTimer>
#include <unistd.h>
worker::worker()
{
qDebug() << "Worker thread" << thread();
running = true;
}
void worker::doWork()
{
qDebug() << "starting doWork thread" << thread();
int i = 0;
QVector<QPoint> v;
QElapsedTimer t;
t.start();
while ((i < 100000) && running)
{
int x = qrand() % 400;
int y = qrand() % 400;
QPoint p(x, y);
bool f = false;
for (int j = 0; j < v.size() && !f; j++)
if (v[i].x() == p.x() && v[i].y() == p.y())
f = true;
if (!f)
{
emit new_point(p);
i++;
}
}
qDebug() << "elapsed time:" << t.elapsed();
qDebug() << "closing doWork thread" << thread();
emit finished();
}
PROBLEMS:
The signal new_point is emitted too fast, hence the scene is not able to keep up updating it, hence the scene is updated in blocks. the only way to make update it smoothly seems to be by adding a usleep(100000) in the for loop, but I don't want to do this, since I really think it is a bad practice.
Checking values in the console as regards the elapsed time in the doWork() method and in the Qdialog thread, it seems that the for loop executes very fast, often in less than 100 milliseconds. The Qdialog thread takes instead much more time to process all the updates, that is, to draw all the points to the scene. Is there a better way to update the scene? I read on some forums to create a QImage and then pass it to the scene, could you provide a simple example for my case?
I can also use QCoreApplication::processEvents() and do all computations in the GUI thread, and in fact the GUI is responsive, the scene updates smoothly. But the time required to draw all the points is much much more than the time required to draw them with a separate thread.
So, what should I do? Thank you in advance.
I have been trying to get this simple example using threads activated by pushbuttons to work. It is based off of the solution in the question below:
How to implement frequent start/stop of a thread (QThread)
The main differences between the example solution above and my code below are:
I used a QWidget instead of MainWindow
I changed the name of signals for clarity
My code contains debugging information
I experimented with eliminating the signals created by worker as the didn't appear to do anything
It appears that the start/stop signals are not triggering their corresponding slots, but I am not experienced enough to troubleshoot why.
Additionally, I am unsure of the purpose of the signal:
SignalToObj_mainThreadGUI()
Is that just something that could be used and is not?
I have been trying to get this code to work for some time, so any help would be greatly appreciated.
main.cpp
#include "threadtest.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ThreadTest w;
w.show();
return a.exec();
}
threadtest.h
#include <QWidget>
#include <QThread>
#include "worker.h"
namespace Ui
{
class ThreadTest;
}
class ThreadTest : public QWidget
{
Q_OBJECT
public:
explicit ThreadTest(QWidget *parent = 0);
~ThreadTest();
signals:
void startWorkSignal();
void stopWorkSignal();
private slots:
void on_startButton_clicked();
void on_stopButton_clicked();
private:
Ui::ThreadTest *ui;
worker *myWorker;
QThread *WorkerThread;
};
threadtest.cpp
#include "threadtest.h"
#include "ui_threadtest.h"
ThreadTest::ThreadTest(QWidget *parent) :
QWidget(parent),
ui(new Ui::ThreadTest)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
connect(this,
SIGNAL(startWorkSignal()),
myWorker,
SLOT(StartWork())
);
connect(this,
SIGNAL(stopWorkSignal()),
myWorker,
SLOT(StopWork())
);
//Debug
this->dumpObjectInfo();
myWorker->dumpObjectInfo();
}
ThreadTest::~ThreadTest()
{
delete ui;
}
void ThreadTest::on_startButton_clicked()
{
qDebug() << "startwork signal emmitted";
emit startWorkSignal();
}
void ThreadTest::on_stopButton_clicked()
{
qDebug() << "stopwork signal emmitted";
emit stopWorkSignal();
}
worker.h
#include <QObject>
#include <QDebug>
class worker : public QObject {
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
~worker();
signals:
void SignalToObj_mainThreadGUI();
//void running();
//void stopped();
public slots:
void StopWork();
void StartWork();
private slots:
void do_Work();
private:
volatile bool running, stopped;
};
worker.cpp
#include "worker.h"
worker::worker(QObject *parent) : QObject(parent), stopped(false),
running(false)
{
qDebug() << "running: " << running;
qDebug() << "stopped: " << stopped;
}
worker::~worker() {}
void worker::do_Work()
{
qDebug() << "inside do Work";
emit SignalToObj_mainThreadGUI();
if (!running || stopped) return;
// actual work here
/*
for (int i = 0; i < 100; i++)
{
qDebug() << "count: " + i;
}
*/
QMetaObject::invokeMethod(this, "do_Work", Qt::QueuedConnection);
}
void worker::StopWork()
{
qDebug() << "inside StopWork";
stopped = true;
running = false;
//emit stopped();
}
void worker::StartWork()
{
qDebug() << "inside StartWork";
stopped = false;
running = true;
//emit running();
do_Work();
}
You should write
WorkerThread->start();
Or you can use the thread of the ThreadTest object instead the WorkerThread (in this case the WorkerThread is needless):
myWorker->moveToThread(thread()); // this->thread
The slots are not triggered, because you have moved myWork to the thread WorkerThread, but didnot run an event loop in that thread. In threadtest.cpp, add
WorkerThread .start();
after
myWorker = new worker;
WorkerThread = new QThread;
myWorker->moveToThread(WorkerThread);
I am doing an exercise qt console application on threading, here is the code:
// make two thread, one checking on the state of the other
//////////////////////////////////////
// main.cpp
#include "mytimer.h"
#include "mythread.h"
#include "checkthread.h"
#include <QCoreApplication>
#include <QString>
#include <QFile>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyThread mThread1;
mThread1.name = "thread 1";
mThread1.start(QThread::HighestPriority);
CheckThread mCheck(&mThread1);
mCheck.start();
return a.exec();
}
///////////////////////////////////////
// mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QtCore>
class MyThread : public QThread
{
public:
MyThread();
void run();
QString name;
bool stop;
int sum;
};
#endif // MYTHREAD_H
//////////////////////////////////////
// mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
sum = 0;
}
void MyThread::run()
{
qDebug() << this->name << " running...";
for(int i=0; i<1000; i++) {
this->sum += i;
qDebug() << this->name << " counting " << sum;
this->sleep(1);
if(this->stop) {
break;
}
}
}
//////////////////////////////////////
// checkthread.h
#ifndef CHECKTHREAD_H
#define CHECKTHREAD_H
#include <QThread>
#include "mythread.h"
class CheckThread : public QThread
{
Q_OBJECT
public:
explicit CheckThread(QObject *parent = 0);
explicit CheckThread(MyThread *tocheck);
void run();
MyThread *tocheck_;
};
#endif // CHECKTHREAD_H
//////////////////////////////////////
// checkthread.cpp
#include "checkthread.h"
CheckThread::CheckThread(QObject *parent) :
QThread(parent)
{
}
CheckThread::CheckThread(MyThread *tocheck) :
tocheck_(tocheck)
{
}
void CheckThread::run() {
while(true) {
this->sleep(1);
if(tocheck_->sum > 15) {
tocheck_->stop = true;
}
}
}
The expected behavior is that mThread1 shoud count to 15 and then stop,
but instead it is stuck at 0.
Interestingly, if I add the following code into the main.cpp file, then it runs
ok:
void Write(QString Filename)
{
QFile fh(Filename);
if(!fh.open(QFile::WriteOnly | QFile::Text))
{
qDebug() << "Could not open file for writing";
return;
}
QTextStream out(&fh);
out << "hi world";
fh.flush();
fh.close();
}
void Read(QString Filename)
{
QFile fh(Filename);
if(!fh.open(QFile::ReadOnly | QFile::Text))
{
qDebug() << "Could not open file for writing";
return;
}
QTextStream in(&fh);
QString mtext = in.readAll();
qDebug() << mtext;
}
I am using qt 4.8 on a kubuntu 13.10 machine, and the ide is qt creator 3.0.1
The problem is the member var stop in mythread.h was not initialized.
But this does not explain why the Read and Write functions in main.cpp solves the problem. Very puzzling.