Run multiple threads and wait all to finish - multithreading

I have the Emitter class that emulates a heavy process.
class Emitter : public QObject
{
Q_OBJECT
public:
explicit Emitter(QObject* parent = nullptr);
~Emitter();
int start();
signals:
void finished(int id);
private slots:
void heavyProcess();
private:
static int globalId;
int id;
QThread t;
void finish();
};
int Emitter::globalId = 1;
Emitter::Emitter(QObject* parent)
: QObject{ parent }
, id{ globalId++ }
{
moveToThread(&t);
t.start();
}
Emitter::~Emitter()
{
qDebug() << "Destruct" << id;
t.quit();
t.wait();
}
int
Emitter::start()
{
QMetaObject::invokeMethod(this, "heavyProcess");
return id;
}
void
Emitter::heavyProcess()
{
auto time{ QRandomGenerator::global()->bounded(1000u, 3000u) };
qDebug() << "Id" << id << "Time" << time;
QThread::msleep(time);
finish();
}
void
Emitter::finish()
{
qDebug() << "Finished" << id;
emit finished(id);
}
Also, there is a manager that creates two instances of the Emitter class. I want to run the heavy process of each instance and when all the instances finishes, the manager emits a finished signal.
class Manager : public QObject
{
Q_OBJECT
public:
explicit Manager(QObject* parent = nullptr);
void start();
signals:
void finished();
public slots:
private slots:
void update(int id);
private:
QHash<int, QSharedPointer<Emitter>> emitters;
};
Manager::Manager(QObject* parent)
: QObject{ parent }
{}
void
Manager::start()
{
QSharedPointer<Emitter> em1{ new Emitter{} };
QSharedPointer<Emitter> em2{ new Emitter{} };
connect(em1.get(), &Emitter::finished, this, &Manager::update);
connect(em2.get(), &Emitter::finished, this, &Manager::update);
emitters.insert(em1->start(), em1);
emitters.insert(em2->start(), em2);
}
void
Manager::update(int id)
{
qDebug() << "update" << id;
emitters.remove(id);
if (emitters.isEmpty()) {
qDebug() << "Empty";
emit finished();
}
}
In the main function I expect to quit the QCoreApplication after the Manager emits its finished signal.
int
main(int argc, char* argv[])
{
QCoreApplication a(argc, argv);
Manager manager;
QObject::connect(&manager, &Manager::finished, &a, &QCoreApplication::quit);
manager.start();
return a.exec();
}
Sometimes I have this output
Id 1 Time 1416
Id 2 Time 1096
Finished 2
update 2
Destruct 2
That means that one instance of the Emitter class is not finished, maybe because the thread is "killed" before it has time to finish their process.
My questions:
Is this a correct approach to create multiple threads to execute the same process with a different set of data?
Why the finished signal of the Manager is called while a thread is running?
If the way I am managing the threads is wrong, could you guide me to do in a better way?

Related

Are we limited to 3300-3400 threads/process in 64-bit Windows system?

Following along Threaded Fortune Server Example I've created a similar server that receives json object from clients and it can handle around 3330 clients simultaneously without any issue.
If I increase the number of clients to 3400, I get:
CreateWindow() for QEventDispatcherWin32 internal window failed (The current process has used all of its system allowance of handles for Window Manager objects.)
Qt: INTERNAL ERROR: failed to install GetMessage hook: 1158, The current process has used all of its system allowance of handles for Window Manager objects.
How can I increase number of threads?
EDIT
Instead of FortuneThread I have ClientThread like this:
class ClientThread : public QThread
{
Q_OBJECT
public:
explicit ClientThread(qintptr socketDescriptor);
void run() override;
signals:
void readyRemove();
private:
qintptr socketDescriptor;
};
and in run() I am creating client:
void ClientThread::run()
{
Client c(socketDescriptor);
emit readyRemove();
}
and the Client is as usual:
class Client : public QTcpSocket
{
Q_OBJECT
public:
explicit Client(qintptr descriptor);
public slots:
void onReadyRead();
void onError(QTcpSocket::SocketError error);
};
and implemented as:
Client::Client(qintptr descriptor)
{
connect(this, &Client::readyRead, this, &Client::onReadyRead);
connect(this, static_cast<void (Client::*)(QAbstractSocket::SocketError)>(&QAbstractSocket::error), this, &Client::onError);
setSocketDescriptor(descriptor);
waitForDisconnected();
}
void Client::onReadyRead()
{
QVariantMap map = QJsonDocument::fromJson(readAll()).object().toVariantMap();
qDebug() << map["Name"].toString() << map["Age"].toInt();
}
void Client::onError(SocketError error)
{
if(state() == ConnectedState && error == SocketTimeoutError)
waitForDisconnected();
}
Ofcourse, onReadyRead() can be problematic in this way. Server class takes care of ClientThread. It stores each client thread in a QVector and removes it when disconnected from host and here is its header:
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(qint16 port, QObject *parent = 0);
~Server();
protected:
void incomingConnection(qintptr descriptor) override;
public slots:
void removeClient();
private:
QVector<ClientThread*> clients;
};
and incomingConnection and removeClient do these:
void Server::incomingConnection(qintptr descriptor)
{
ClientThread *thread = new ClientThread(descriptor);
connect(thread, &ClientThread::readyRemove, this, &Server::removeClient);
thread->start();
clients.push_back(thread);
}
void Server::removeClient()
{
ClientThread *thread = (ClientThread*)sender();
thread->quit();
thread->deleteLater();
clients.removeOne(thread);
}
To test the Server application I've used the following Console application:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QVector<QTcpSocket*> clients;
for(int i = 0; i < 3300; i++){
QTcpSocket *sock = new QTcpSocket();
sock->connectToHost("127.0.0.1", 2000);
sock->waitForConnected();
clients.push_back(sock);
}
QJsonObject obj;
obj["Name"] = QString("TestName");
obj["Age"] = 1;
QJsonDocument doc(obj);
foreach (QTcpSocket *s, clients) {
s->write(doc.toJson());
s->flush();
}
foreach (QTcpSocket *s, clients) {
s->close();
s->deleteLater();
}
}

How to connect signal from qconcurrent thread to gui thread sharing one string

I'm trying to update gui label with an other thread information (QString).
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public Q_SLOTS:
void sl_appendInfo(QString p_text);
private:
Ui::MainWindow *ui;
QFuture<void> m_thread;
QFuture<void> m_engine;
engine* m_object;
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
m_object = new engine();
qRegisterMetaType<QString>();
bool success = connect(this->m_object, SIGNAL(engine::sig_appendInfo(QString)), this, SLOT(sl_appendInfo(QString)), Qt::QueuedConnection);
if(!success)
{
qDebug("success failed");
}
m_engine = QtConcurrent::run(this->m_object, &engine::eventLoop);
}
//slot declaration in mainwindow.cpp
void MainWindow::sl_appendInfo(QString p_text)
{
ui->label->setText(p_text.toLocal8Bit().constData());
}
class engine : public QObject
{
Q_OBJECT
public:
engine();
~engine();
void eventLoop();
Q_SIGNALS:
void sig_exitengine(void);
void sig_appendInfo(QString p_text);
};
void engine::eventLoop()
{
int state = false;
while(true)
{
state = getNextEvent(m_event);
if (state == true)
{
sig_appendInfo("information for gui: we handled a new event !");
state=false;
}
QThread::msleep(1000);
}
}
Now I use this link : My signal / slot connection does not work to build my own code but it didn't work, the connection failed... Can I have some help please?
Thank you
Your connect syntax is wrong. You shouldn't include the class name in the SIGNAL macro. If you use the old syntax, it should be:
bool success = connect(m_object, SIGNAL(sig_appendInfo(QString)), this, SLOT(sl_appendInfo(QString)), Qt::QueuedConnection);
Or if you want to use the new syntax:
bool success = connect(m_object, &engine::sig_appendInfo, this, &MainWindow::sl_appendInfo, Qt::QueuedConnection);

How to connect the signal and slot in multi-thread program?

The mainwindow.cpp:
#include "ui_mainwindow.h"
#include <QtCore>
/* ****** Thread part ****** */
myThread::myThread(QObject *parent)
: QThread(parent)
{
}
void myThread::run()
{
while(1){
qDebug("thread one----------");
emit threadSignal1();
usleep(100000);
}
exec();
}
myThread2::myThread2(QObject *parent)
: QThread(parent)
{
}
void myThread2::run()
{
while(1){
qDebug("thread two");
emit threadSignal2();
usleep(100000);
}
exec();
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
onethread = new myThread(this);
onethread->start(QThread::NormalPriority);
twothread = new myThread2(this);
twothread->start(QThread::NormalPriority);
connect(onethread, SIGNAL(onethread->threadSignal1()),
this, SLOT(mySlot1()));
connect(twothread, SIGNAL(threadSignal2()),
this, SLOT(mySlot2()),Qt::QueuedConnection);
}
void MainWindow::mySlot1()
{
ui->textEdit1->append("This is thread1");
}
void MainWindow::mySlot2()
{
ui->textEdit1->append("This is thread2");
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_clicked()
{
ui->textEdit1->append("textEdit1");
ui->textEdit2->append("textEdit2");
}
The mainwindow.h:
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
namespace Ui {
class MainWindow;
}
class myThread : public QThread
{
Q_OBJECT
public:
myThread(QObject *parent = 0);
void run();
signals:
void threadSignal1();
};
class myThread2 : public QThread
{
Q_OBJECT
public:
myThread2(QObject *parent = 0);
void run();
signals:
void threadSignal2();
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void mySlot1();
void mySlot2();
private slots:
void on_pushButton_clicked();
private:
Ui::MainWindow *ui;
myThread *onethread;
myThread2 *twothread;
};
#endif // MAINWINDOW_H
Please check the above code. It can give the qDebug output normally while the textEdit1/2 have no any output. And it seems to be a multi-thread signal/slot connect issue. Who can help? Thanks!
You need to define the slots as slots:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
void mySlot1();
void mySlot2();
...
Also a few other things. You don't need the exec() call in each of the threads. They are going into an endless loop anyways and would never reach that statement. And, the purpose of exec() is to start an event loop in the thread so that it can receive and process events, like having its own slots. You are just emitting.
Are you sure this connection works?
connect(onethread, SIGNAL(onethread->threadSignal1()),
this, SLOT(mySlot1()));
It should probably be:
connect(onethread, SIGNAL(threadSignal1()),
this, SLOT(mySlot1()));
I believe QueuedConnection will be implied for the connections because they are targeting a slot on an owner in a different thread than the emitter.

How to implement frequent start/stop of a thread (QThread)

I need to start and stop a thread very frequently using push button..I am using Qt. Recently I learned to create a QObject of the worker and move it to the object of the QThread as the correct way of implementing threads in Qt. Following is my implementation...
Worker.h
class worker : public QObject
{
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
void StopWork();
void StartWork();
bool IsWorkRunning();
signal:
void SignalToObj_mainThreadGUI();
public slots:
void do_Work();
private:
void Sleep();
volatile bool running,stopped;
QMutex mutex;
QWaitCondition waitcondition;
};
Worker.cpp
worker::worker(QObject *parent) :
QObject(parent),stopped(false),running(false)
{
}
void worker::do_Work()
{
running = true;
while(!stopped)
{
emit SignalToObj_mainThreadGUI();
Sleep();
}
}
void worker::Sleep()
{
mutex.lock();
waitcondition.wait(&mutex,10);
mutex.unlock();
}
void worker::StopWork()
{
mutex.lock();
stopped = true;
running = false;
mutex.unlock();
}
void worker::StartWork()
{
mutex.lock();
stopped = false;
running = true;
mutex.unlock();
}
bool worker::IsWorkRunning()
{
return running;
}
MainWindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
private slots:
void on_pushButton_push_to_start_clicked();
void on_pushButton_push_to_stop_clicked();
private:
Ui::MainWindow *ui;
worker *myWorker;
QThread *WorkerThread;
};
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker.moveToThread(WorkerThread);
QObject::connect(WorkerThread,SIGNAL(started()),myWorker,SLOT(do_Work()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_push_to_start_clicked()
{
if(!myWorker.IsWorkRunning())
{
myWorker->StartWork();
WorkerThread->start();
}
}
void MainWindow::on_pushButton_push_to_stop_clicked()
{
if(myWorker.IsWorkRunning())
{
myWorker->StopWork();
WorkerThread->quit();
}
}
Initially the application works fine but after some operations of the push button to turn the working of the thread on and off the following error comes up...
QObject::killTimers(): timers cannot be stopped from another thread
I am quite puzzled at this error...do I need to implement the start/stop functions as signals and slots rather than member functions of the class?
Don't start and stop WorkerThread. Just leave it running. Also, move your StartWork() and StopWork() methods to the public slots section. You really don't need the mutex at all.
Worker.h
class worker : public QObject
{
Q_OBJECT
public:
explicit worker(QObject *parent = 0);
signal:
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
worker::worker(QObject *parent) :
QObject(parent), stopped(false), running(false)
{}
void worker::do_Work()
{
emit SignalToObj_mainThreadGUI();
if ( !running || stopped ) return;
// do important work here
// allow the thread's event loop to process other events before doing more "work"
// for instance, your start/stop signals from the MainWindow
QMetaObject::invokeMethod( this, "do_Work", Qt::QueuedConnection );
}
void worker::StopWork()
{
stopped = true;
running = false;
emit stopped();
}
void worker::StartWork()
{
stopped = false;
running = true;
emit running();
do_Work();
}
MainWindow.h
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void startWork();
void stopWork();
private slots:
void on_pushButton_push_to_start_clicked();
void on_pushButton_push_to_stop_clicked();
private:
Ui::MainWindow *ui;
worker *myWorker;
QThread *WorkerThread;
};
MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
myWorker = new worker;
WorkerThread = new QThread;
myWorker.moveToThread(WorkerThread);
connect( this, SIGNAL(startWork()), myWorker, SLOT(StartWork()) );
connect( this, SIGNAL(stopWork()), myWorker, SLOT(StopWork()) );
}
void MainWindow::on_pushButton_push_to_start_clicked()
{
emit startWork();
}
void MainWindow::on_pushButton_push_to_stop_clicked()
{
emit stopWork();
}

why signal/slot not working with multiple threads?

class A : public QObject{
Q_OBJECT
signals:
void a_sig();
public:
A(){ }
public slots:
void begin(){
QObject::connect(&_timer, SIGNAL(timeout()), this, SIGNAL(a_sig()));
_timer.start(1000);
}
private:
QTimer _timer;
};
class B : public QObject{
Q_OBJECT
public:
B(){ value = 0; }
public slots:
void b_slot(){
++value;
QFile file("out.txt");
file.open(QIODevice::WriteOnly);
QTextStream out(&file);
out << value << "\n";
file.close();
}
private:
int value;
};
int main(int argc, char **argv){
QCoreApplication app(argc, argv);
A a;
B b;
QThread aThread;
QThread bThread;
QObject::connect(&aThread, SIGNAL(started()), &a, SLOT(begin()));
QObject::connect(&a, SIGNAL(a_sig()), &b, SLOT(b_slot()));
a.moveToThread(&aThread);
b.moveToThread(&bThread);
aThread.start();
bThread.start();
return app.exec();
}
I'm trying to understand why b_slot() isn't getting called. Can anyone explain what's happening, and why b_slot() isn't getting called?
The problem is the ownership of the _timer member of the A class.
Since you're not explicitly initializing it, it is initialized without a parent object. So the a.moveToThread(&aThread) isn't moving the timer to aThread, and things get confused after that.
Change A's constructor to:
A() : _timer(this) {}
and your b_slot() will get called.
The problem is that while object a is moved to aThread, _timer object still belongs to the original main thread. Try initializing _timer inside begin method like that:
void begin() {
_timer = new QTimer;
QObject::connect(_timer, SIGNAL(timeout()), this, SIGNAL(a_sig()));
_timer->start(1000);
}
private:
QTimer *_timer;

Resources