Qt signal slot over thread, is this the safe way? - c++

Say, I have 2 threads: A and B, A is the main thread. In thread A, there are two on_button_click slots. The first one is:
on_button_one_clicked(){
myObject_one = new myObject(this);
myObject_one->setParent(map);
myObject_two = new myObject(this);
myObject_two->setParent(map);
...
}
The second one is:
on_button_two_clicked(){
foreach(myObject* i, map->childItems){
delete i;
}
}
Here, myObject and map are all QGraphicsItems. In thread B, a signal is emitted to trigger a slot of thread A:
slot_triggered_by_signal_from_thread_B(){
foreach(myObject* i, map->childItems){
i->do_some_thing();
}
}
Is this safe? Will it happen, when the codes comes to the line i->do_some_thing and meet an empty pointer and crash?

It is safe as long as you use the auto connection or queued conncetion type between the threads since then the slot will be only invoked when your other slot is finishing up or vice versa. They will not be running "simultaneously". That is the only concern that I could imagine for this not to be safe enough. I believe you meant a scenario like this:
main.cpp
#include <QThread>
#include <QApplication>
#include <QTimer>
#include <QObject>
#include <QPushButton>
#include <QDebug>
class Work : public QObject
{
Q_OBJECT
public:
explicit Work(QObject *parent = Q_NULLPTR) : QObject(parent) { QTimer::singleShot(200, this, SLOT(mySlot())); }
public slots:
void mySlot() { emit mySignal(); }
signals:
void mySignal();
};
class MyApplication : public QApplication
{
Q_OBJECT
public:
explicit MyApplication(int argc, char **argv)
: QApplication(argc, argv)
, pushButton(new QPushButton())
{
QStringList stringList{"foo", "bar", "baz"};
QThread *workerThread = new QThread();
Work *work = new Work();
work->moveToThread(workerThread);
connect(pushButton, &QPushButton::clicked, [&stringList] () {
for (int i = 0; i < stringList.size(); ++i)
stringList.removeAt(i);
});
connect(work, &Work::mySignal, [&stringList] () {
for (int i = 0; i < stringList.size(); ++i)
qDebug() << stringList.at(i);
});
}
~MyApplication()
{
delete pushButton;
}
QPushButton *pushButton;
};
#include "main.moc"
int main(int argc, char **argv)
{
MyApplication application(argc, argv);
return application.exec();
}
main.pro
TEMPLATE = app
TARGET = main
QT += widgets
CONFIG += c++11
SOURCES += main.cpp
Build and Run
qmake && make && ./main

Let's assume that main thread does some heavy work in on_button_two_clicked function.
Any other actions include user do something or another request from other thread
(in this case slot_triggered_by_signal_from_thread_B) will be blocked until
finishing on_button_two_clicked.
I think it means that it guarantees finishing previous event.
In conclusion, it is safe!

Related

QObject::~QObject: Timers cannot be stopped from another thread

I have this very simple Qt code:
void thread_func()
{
int a1 = 1;
const char* a2[] = { "dummy_param" };
QApplication app(a1, (char**)a2);
QMessageBox msg(QMessageBox::NoIcon, "MyTitle", "Foo bar Foo bar", QMessageBox::Ok);
msg.exec();
}
If I call the above function from my main in a std::thread, it brings up the dialog:
int main()
{
std::thread t(thread_func);
t.join();
}
...but when I close it, I get the warning message:
QObject::~QObject: Timers cannot be stopped from another thread
I've checked that the thread affinity of both QApplication instance and msg is the same. Calling the thread_func function directly from my main() (without creating a std::thread) removes that message.
I am using Qt 5.15.1 on Windows 10.
What am I missing here? Thanks
It's not allowed to operate Qt GUI directly outside the main thread(GUI thead). You can emit signals.
The warning message says it all. Use a signal/slot mechanism to accomplish the same thing.
#include <QApplication>
#include <QMessageBox>
#include <QObject>
#include <QThread>
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(){}
public slots:
void displayMessageBox()
{
QMessageBox msg(QMessageBox::NoIcon, "MyTitle", "Foo bar Foo bar", QMessageBox::Ok);
msg.exec();
this->close();
}
};
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker() {}
void start() { emit askForMessageBox(); }
signals:
void askForMessageBox();
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget *widget;
Worker *worker;
QThread *thread(nullptr);
widget = new Widget();
worker = new Worker();
thread = new QThread(nullptr);
QObject::connect(worker, &Worker::askForMessageBox, widget, &Widget::displayMessageBox);
QObject::connect(thread, &QThread::started, worker, &Worker::start);
widget->show();
worker->moveToThread(thread);
thread->start();
return a.exec();
}

Why is this thread slot not getting called?

Two.h
#ifndef TWO_H
#define TWO_H
#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTimer>
class Two : public QObject
{
Q_OBJECT
private:
QTimer abc;
public:
QString m_xyz;
Two();
signals:
void emitThisSignal( int x, QString &y );
public slots:
void mySlot();
};
class Controller : public QObject
{
Q_OBJECT
private:
Two objTwo;
QThread objQThread;
Controller();
public slots:
void mySlot( int x, QString &y)
{
qDebug() << "\nWWWWWWWWWWWWW: " << y;
}
};
#endif // TWO_H
Two.cpp
#include "two.h"
Two::Two()
{
m_xyz = "aksja";
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &Two::mySlot);
timer->start(1000);
}
void Two::mySlot()
{
emit emitThisSignal(4, m_xyz);
qDebug()<< "FFFFFFFFFFF " << m_xyz;
}
Controller::Controller()
{
objTwo.moveToThread( &objQThread );
connect( &objTwo, &Two::emitThisSignal, this, &Controller::mySlot );
connect( &objQThread, &QThread::finished, &objQThread, &QThread::deleteLater );
objQThread.start();
}
Controller::~Controller()
{
delete objTwo;
objQThread.wait();
}
I can see that the signal is being emitted because of the print statement but the slot of the Controller class is not getting called.
void Two::mySlot()
{
emit emitThisSignal(4, m_xyz);
qDebug()<< "FFFFFFFFFFF " << m_xyz;
}
Why is that so?
int main( int argc, char* argv[])
{
QCoreApplication app(argc, argv);
Controller o;
return app.exec();
}
See documentation of QObject::connect, note last argument with default value: Qt::AutoConnection.
Its documentation says:
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.
Now you are fall in into Qt::QueuedConnection scenario:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
So basically you need something which will provide an event loop.
In this code you need that:
int main( int argc, char* argv[])
{
QCoreApplication app{argc, argv};
Controller o;
// note you need something what will stop this event loop to terminate application
return app.exec();
}
One more thing.
Now I noticed that your signals and slot argument is quite unusual. Problem might be second argument which type is QString&.
It might be source of problems I do not know if Qt is able to marshal non const references. If you will add const then it will be able to marshal QString and should work (if I didn't missed other pitfall).

Qt Slot is not called when it lives on a thread created via std::async

I wanted to create a Qt widget which communicates with other classes on different threads via the signal / slot system. The recieving Objects are created in a Function wich is run via std::async.
The problem is: If the widget emits a signal the slot on the other thread is not called.
My Example:
I created the Class MainWindow which derives from QMainWindow and will live on the main thread. The class Reciever is created in a function which is called via std::async, and has a thread which should print something to the console.
I tested if the signal is emitted by connecting it to another slot on the same thread which works fine.
MainWindow.hpp
#pragma once
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
signals:
void send();
private slots:
void buttonClicked();
void recieve();
};
MainWindow.cpp
#include "MainWindow.hpp"
#include <iostream>
#include <QPushButton>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
QPushButton* start = new QPushButton("Start");
setCentralWidget(start);
start->show();
connect(start, SIGNAL(clicked(bool)), this, SLOT(buttonClicked()));
connect(this, SIGNAL(send()), this, SLOT(recieve()));
}
void MainWindow::buttonClicked()
{
std::cout << "MainWindow::buttonClicked()\n";
emit send();
}
void MainWindow::recieve()
{
std::cout << "MainWindow::recieve()\n";
}
Reciever.hpp
#include <QObject>
class Reciever : public QObject
{
Q_OBJECT
public:
Reciever(QObject *parent = 0);
public slots:
void recieve();
};
Reciever.cpp
#include "Reciever.hpp"
#include <iostream>
Reciever::Reciever(QObject *parent) : QObject(parent)
{
std::cout << "Reciever()" << std::endl;
}
void Reciever::recieve()
{
std::cout << "Reciever::recieve()" << std::endl;
}
main.cpp
#include "MainWindow.hpp"
#include "Reciever.hpp"
#include <QApplication>
#include <future>
void StartAndConnect(MainWindow &widget)
{
Reciever* rec = new Reciever();
QObject::connect(&widget, SIGNAL(send()), rec, SLOT(recieve()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow myWidget;
myWidget.show();
auto future = std::async(std::launch::async, [&myWidget](){
StartAndConnect(myWidget);
});
app.exec();
future.wait();
}
After some research my strongest guess was, that the thread launched by std::async does not has a Qt event-loop and thus will not come to a point where the posted event (emit) is processed. I changed the main to use QtConcurrent::run but it also did not work.
EDIT
Here my try with QtConcurrent:
main2.cpp
#include "MainWindow.hpp"
#include "Reciever.hpp"
#include <QApplication>
#include <future>
#include <QtConcurrent>
void StartAndConnect(MainWindow &widget)
{
Reciever* rec = new Reciever();
QObject::connect(&widget, SIGNAL(send()), rec, SLOT(recieve()));
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow myWidget;
myWidget.show();
auto future = QtConcurrent::run( [&myWidget](){
StartAndConnect(myWidget);
});
app.exec();
future.waitForFinished();
}
You need a running event loop in your thread, if you want to process cross-thread slot calls.
auto future = QtConcurrent::run( [&myWidget](){
StartAndConnect(myWidget);
QEventLoop e;
e.exec();
});
But I recomend to use QThread, because in your case it is obvious. Qt has a very good documentation, that describes your case.

Qt Script Multithreading

I need to run multiple (up to 50 or more) Qt Script Functions concurrently. Running two or three Threads with Script Functions works just fine, but as soon as I run around 50 Threads, I get an Error and my Program crashes.
ASSERTION FAILED: globalData().dynamicGlobalObject (..\3rdparty\javascriptcore\JavaScriptCore\runtime/JSGlobalObject.h:411 QTJSC::JSGlobalObject* QTJSC::ExecState::dynamicGlobalObject())
My main.cpp looks like this:
#include <QCoreApplication>
#include <QScriptEngine>
#include <threadworker.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QScriptEngine engine;
QScriptValue val = engine.evaluate("(function() {var r = Math.random(); while(1>0) print(r);})");
ThreadWorker *worker[50];
for(int i=0;i<50;i++) {
worker[i] = new ThreadWorker(val);
QObject::connect(worker[i], SIGNAL(needsStarting()), worker[i], SLOT(startScript()));
emit worker[i]->needsStarting();
}
return a.exec();
}
This is my threadworker.h:
#ifndef THREADWORKER_H
#define THREADWORKER_H
#include <QObject>
#include <QScriptValue>
#include <QThread>
class ThreadWorker : public QObject
{
Q_OBJECT
public:
explicit ThreadWorker(QObject *parent = 0);
explicit ThreadWorker(QScriptValue function);
signals:
needsStarting();
public slots:
void startScript();
private:
QScriptValue value;
QThread thread;
};
#endif // THREADWORKER_H
This is my threadworker.cpp:
#include "threadworker.h"
#include <QDebug>
ThreadWorker::ThreadWorker(QObject *parent) : QObject(parent)
{
}
ThreadWorker::ThreadWorker(QScriptValue function)
{
value = function;
this->moveToThread(&thread);
thread.start();
}
void ThreadWorker::startScript()
{
value.call();
}
I expected, that independently of the amount, the Qt Script Threads would run just fine and I can't understand what is causing this contrary behaviour.
Putting the QScriptEngine on worker class and let moveToThread move it to the the worker thread, seems to solve:
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0);
public slots:
void startScript(const QString &function);
private:
QScriptEngine engine;
QScriptValue value;
};
However, it will be a challenge create so many threads and release them properly on application exit. I suggest you to use pooled threads, for example, QtConcurrent. QtConcurrent allows you (for example but not limited to) multithread with just functions, not necessarly classes, and with QFutureSyncronyzer you can in one call wait for all the threads you want to finish. See QtConcurrent:
#include <QtScript>
#include <QtConcurrent>
#define THREADS 50
void worker_function(const QString &function)
{
QScriptEngine engine;
QScriptValue value;
value = engine.evaluate(function);
value.call();
}
...
QFutureSynchronizer<void> synchronizer;
//Set the max pooled threads
QThreadPool::globalInstance()->setMaxThreadCount(THREADS);
//Start all threads and add them to the future synchronizer
for (int i = 0; i < THREADS; i++)
synchronizer.addFuture(QtConcurrent::run(worker_function, QString("(function() {var r = Math.random(); while(1>0) print(r);})")));
//Wait for all threads to finish
synchronizer.waitForFinished();
...

my thread is not working well it gives all result, together at last not one by one & GUI is got hanged during thread run?

I want to search files by name in a particular location as selected by the user. I want that as soon as I got the file. It must be put in QTreeWidget parallely and showing a QMovie(":/images/img_searching.gif") while searching is in progress until user did not stop searching.
ThreadSearch.h
#ifndef QTHREADSEARCH_H
#define QTHREADSEARCH_H
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QFileInfoList>
class QThreadSearchFileName : public QThread
{
Q_OBJECT
public:
QThreadSearchFileName(QObject *parent = 0);
~QThreadSearchFileName();
void run();
void getAllfiles(QStringList, QDir);
signals:
void fileInfoList(QFileInfo);
private:
QMutex m_Mutex;
QWaitCondition m_WaitCondition;
};
#endif
ThreadSearch.cpp
#include "ThreadSearch.h"
#include <QApplication>
#include <QThread>
QThreadSearchFileName::QThreadSearchFileName(QObject *parent):QThread(parent)
{
}
QThreadSearchFileName::~QThreadSearchFileName()
{
m_WaitCondition.wakeOne();
wait();
}
void QThreadSearchFileName::run()
{
QMutexLocker locker(&m_Mutex);
}
void QThreadSearchFileName::getAllfiles(QStringList targetStrList, QDir currentdir)
{
for(long int i1=0; i1<targetStrList.size(); i1++)
{
QString targetStr;
targetStr = targetStrList[i1];
QDirIterator it(currentdir, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString filename = it.next();
QFileInfo file(filename);
if (file.isDir())
{ // Check if it's a dir
continue;
}
if (file.fileName().contains(targetStr, Qt::CaseInsensitive))
{
emit fileInfoList(file);
}
}
}
}
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include <QDirIterator>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QThreadSearchFileName *m_pSearchFileNameThread = new QThreadSearchFileName;
for(int i=0; i<userSelectedpathList.size(); i++)
{
QDir dir(userSelectedpathList[i]);
m_pSearchFileNameThread ->getAllfiles(stringListToBeSearch, dir);
connect(m_pSearchFileNameThread,SIGNAL(fileInfoList(QFileInfo)),this,SLOT(searchFileNameResult(QFileInfo)));
}
return a.exec();
}
void main::searchFileNameResult(QFileInfo file1) //Now Making SearchFile Name Tree
{
QTreeWidgetItem *SearchTreeItem = new QTreeWidgetItem(m_psearchProgresswdgt->finalSearchList_treeWidget);
SearchTreeItem->setCheckState(0,Qt::Unchecked);
SearchTreeItem->setText(1,file1.baseName());
}
It is good practice to separate such operations out from GUI object. What is more I would suggest higher-level async mechanism provided by QObject:
Make some class which could handle searching, for example
SearchingClass:
class SearchingClass : public QObject {
Q_OBJECT
public:
void setSomeSearchParametersOrSomething(QObject* something);
public slots:
void search();
signals:
void found(QObject* objectThatHasBeenFound);
}
Create instance of this class and move it into another thread:
auto searchingObject = new SearchingClass();
searchingObject->setSomeSearchParametersOrSomething(...);
auto thread = new QThread();
searchingObject->moveToThread(thread);
connect(this, SIGNAL(startSearchingSignal()), searchingObject, SLOT(search()));
connect(searchingObject, SIGNAL(found(QObject*)), this, SLOT(someHandleFoundSlot(QObject*)));
emit startSearchingSignal();
Make sure that found signal is being emitted every time that searching algorithm finds some result.
Ofc you must implement someHandleFoundSlot and declarate startSearchingSignal signal in GUI class.
I assume that you barely know Qt framework, so you should read about signals and slots as well as Qt meta-object system to fully understand whole code.
EDIT:
I see that you have edited your question. Your problem has several solution, I will describe you, what you did wrong comparably to what I had posted here.
Do not extend QThread. Extend QObject instead. It makes you can call moveToThread method. Crete an instance of QThread and pass it to this method. It causes later execution of slots to be performed in the thread you passed.
Do not make identical connections in loop until you want it to be executed more than once.
Make method getAllfiles (or search in my example) to be slot and do not call it manually. When you call method manually, it will always be performed in the same thread. Just connect it to some signal, and emit that signal.
[Just like you emit signal when you find matching file – the result is being handled in the slots objects thread.]
It's your decision, if you want to have thread for every single userSelectedpathList element. I would advice you to do it in one working thread (it's disc operations, I think it wouldn't be faster) and iterate that list inside getAllfiles method.
Previously I gave you generic answer about "how to do async work in Qt". Here's a simple implementation for your use case (I hope you will learn something).
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QFileInfo>
#include <QDirIterator>
#include <QThread>
#include <QDebug>
#include <QPushButton>
#include "filesearchingclass.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0){
setCentralWidget(&pushButton);
connect (&pushButton, SIGNAL(clicked(bool)), this, SLOT(main())); //invoke this->main() in event loop when button is pressed
qRegisterMetaType<QFileInfo>("QFileInfo");
searchingThread.start(); //thread must be started
}
~MainWindow(){
searchingThread.quit();
searchingThread.wait();
}
public slots:
void main() {
//EXAMPLE PARAMETERS:
QStringList pathList;
pathList.append("/home");
pathList.append("/var/log");
QStringList stringListToBeSearch;
stringListToBeSearch.append(".jpg");
stringListToBeSearch.append(".log");
//-------------------
auto fileSearchingObject = new FileSearchingClass(); //dynamic as you can't destroy object when it is out of scope
fileSearchingObject->moveToThread(&searchingThread); //important!!!
fileSearchingObject->setTargetStrList(stringListToBeSearch);
fileSearchingObject->setPaths(pathList);
connect(this,SIGNAL(startSearching()),fileSearchingObject,SLOT(search())); //do not call fileSearchingObject->search() manually
connect(fileSearchingObject,SIGNAL(foundFile(QFileInfo)),this,SLOT(searchFileNameResult(QFileInfo))); //handle every result in event loop
connect(fileSearchingObject, SIGNAL(searchFinished()), fileSearchingObject, SLOT(deleteLater())); //no need to wory about deleting fileSearchingObject now
emit startSearching(); //like calling fileSearchingObject->search() but in another thread (because of connection)
}
signals:
void startSearching();
public slots:
void searchFileNameResult(QFileInfo fileInfo) {
//do something
qDebug() << "---FOUND---" << fileInfo.absoluteFilePath() << "\n";
}
private:
QThread searchingThread;
QPushButton pushButton;
};
#endif // MAINWINDOW_H
filesearchingclass.h
#ifndef FILESEARCHINGCLASS_H
#define FILESEARCHINGCLASS_H
#include <QFileInfo>
#include <QDirIterator>
#include <QThread>
#include <QDebug>
class FileSearchingClass : public QObject {
Q_OBJECT
public:
~FileSearchingClass(){}
void setPaths (const QStringList &paths) {
userSelectedPathList = paths;
}
void setTargetStrList(const QStringList &value) {
targetStrList = value;
}
public slots:
void search() {
for(int i=0; i<userSelectedPathList.size(); i++) {
QDir currentDir(userSelectedPathList[i]);
for(long int i1=0; i1<targetStrList.size(); i1++)
{
QString targetStr;
targetStr = targetStrList[i1];
QDirIterator it(currentDir, QDirIterator::Subdirectories);
while (it.hasNext())
{
QString filename = it.next();
QFileInfo file(filename);
if (file.isDir())
{ // Check if it's a dir
continue;
}
if (file.fileName().contains(targetStr, Qt::CaseInsensitive))
{
emit foundFile(file); //calling MainWindow::searchFileNameResult directly is possible, but bad idea. This thread is only focused in searching, not modifing widgets in GUI.
}
}
}
}
emit searchFinished(); //it's always good to know when job is done.
}
signals:
void foundFile(QFileInfo);
void searchFinished();
private:
QStringList userSelectedPathList;
QStringList targetStrList;
};
#endif // FILESEARCHINGCLASS_H