One thread with multiple worker processes? - multithreading

I'm wondering whats the procedure for having one thread and belonging to that thread one worker which has multiple processes. If that doesn't make too much sense hopefully the code below should explain:
// WORKER.H //
class Worker : public QObject
{
Q_OBJECT
Public:
Worker(QObject *parent = nullptr);
Public Slots:
void process1(int x);
void process2(int y);
void process3(int z);
signals:
void sendResult(int result);
}
// CONTROLLER.H //
class controller : public QObject
{
Q_OBJECT
public:
controller(QObject *parent = nullptr);
signals:
operate();
public slots:
void storeResults(const int);
private:
QThread myThread;
}
// CONTROLLER.CPP // I shall only give the constructor
Controller::Controller(QObject *parent) : QObject(parent)
{
Worker *worker = new Worker;
worker->moveToThread(&myThread);
//connections
connect(&myThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::process1);
connect(this, &Controller::operate, worker, &Worker::process2);
connect(this, &Controller::operate, worker, &Worker::process3);
connect(worker, &Worker::sendResult, this, &Controller::storeResult);
myThread.start();
}
What I'm hoping to achieve here is a system where the three processes slots in the worker class will receive data from signals from the GUI. these signals will be constantly being received throughout the life of the program. Once a given signal is received, it shall be processed, and the result of the processing will be emitted into the store result slot.
Would this code achieve what I expect or is there something I'm overlooking?
Many Thanks

Related

QProcess in thread works but with two type output errors

I'am learning QT multithreading.
With the following code, it works ok, but there are two type errors
The code is too simple to figure out what's going wrong.
CmdController(Controller thread) -> CMD(Ping test thread) -> QProcess(excute "ping")
main.c
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// create Test object, and then execute “ping” cmd under windows
CmdController Test;
Test.process();
return a.exec();
}
CmdController.h
// deal with "ping" command
class Cmd : public QObject
{
Q_OBJECT
public:
explicit Cmd();
private:
QProcess CmdExe;
public slots:
void process();
void readFromStdOut();
};
// Controller class
class CmdController : public QObject
{
Q_OBJECT
public:
explicit CmdController();
~CmdController();
signals:
void process();
private:
QThread mThread;
Cmd mCmd;
};
CmdController.c
Cmd::Cmd()
{
connect(&CmdExe, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdOut()));
}
void Cmd::process()
{
CmdExe.start("ping", {"www.baidu.com"});
}
void Cmd::readFromStdOut()
{
QByteArray resault = CmdExe.readAllStandardOutput();
qDebug() << QTextCodec::codecForName("GBK")->toUnicode(resault);
}
CmdController::CmdController()
{
mCmd.moveToThread(&mThread);
connect(this, &CmdController::process, &mCmd, &Cmd::process);
mThread.start();
}
CmdController::~CmdController()
{
mThread.exit();
mThread.wait();
}
The output:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x6ffdf0), parent's thread is QThread(0x1f6510), current thread is QThread(0x6ffdd0)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x6ffdf0), parent's thread is QThread(0x1f6510), current thread is QThread(0x6ffdd0)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x6ffdf0), parent's thread is QThread(0x1f6510), current thread is QThread(0x6ffdd0)
"\r\n正在 Ping www.a.shifen.com [180.101.49.42] 具有 32 字节的数据:\r\n"
"来自 180.101.49.42 的回复: 字节=32 时间=31ms TTL=53\r\n"
"来自 180.101.49.42 的回复: 字节=32 时间=31ms TTL=53\r\n"
"来自 180.101.49.42 的回复: 字节=32 时间=30ms TTL=53\r\n"
"来自 180.101.49.42 的回复: 字节=32 时间=30ms TTL=53\r\n\r\n180.101.49.42 的 Ping 统计信息:\r\n 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),\r\n往返行程的估计时间(以毫秒为单位):\r\n 最短 = 30ms,最长 = 31ms,平均 = 30ms\r\n"
QObject::connect: Cannot queue arguments of type 'HANDLE'
(Make sure 'HANDLE' is registered using qRegisterMetaType().)
Error 1st:
1-6 lines output error, shows creating children object fails, by three times.
The ping test is ok.
so, who cause the error?
QThread(0x6ffdd0) is Cmd-object,
QThread(0x1f6510) is CmdController-object,
QProcess(0x6ffdf0) clearly is QProcess-object,
does it mean the QProcess, try to create something?
Error 2ed:
last two lines.
googled, cann't find 'HANDLE' what it is.
If if remove multithreading stuff from
CmdController(Controller thread) -> CMD(Ping test thread) -> QProcess(excute "ping")
to
CmdController(Controller thread) -> QProcess(excute "ping")
there will be no error, everything works perfectly.
I'am confused.
Thank you.
To understand the problem, one must first be clear that a QObject must be executed only in the thread to which it belongs, if another thread is executed then Qt does not guarantee that it works, and that is what has happened in this case.
Let's analyze to which thread each QObject belongs, by default QObject belongs to the thread where it was created or to the thread of its father, so when you create the object "Test" this belongs the main thread, the object "mCmd" at the beginning belongs to the main thread and then you move it to "mThread", "CmdExe" belongs to the thread where it was created, that is to say to the main thread but the "Cmd" process method is executed in the secondary thread but "CmdExe" is executed in the main thread causing the problem.
The solution is to make "CmdExe" the son of "Cmd" so that both belong to the same thread and not generate problems.
// ...
class Cmd : public QObject
{
Q_OBJECT
public:
explicit Cmd(QObject *parent=nullptr);
private:
QProcess *CmdExe;
public slots:
void process();
void readFromStdOut();
};
// ...
// ...
Cmd::Cmd(QObject *parent):QObject(parent), CmdExe(new QProcess(this))
{
connect(CmdExe, &QProcess::readyReadStandardOutput, this, &Cmd::readFromStdOut);
}
void Cmd::process()
{
CmdExe->start("ping", {"www.baidu.com"});
}
// ...

Failed to start QThread when using QNetworkAccessManager

I'm currently trying to make a software that downloads a lot of files from Google Drive. Downloading is currently not a problem.
Nevertheless, I encounter an issue when launching 500+ simultaneous downloads. I use a slightly modified version of this tutorial : https://wiki.qt.io/Download_Data_from_URL.
Here is the .h file :
class FileDownloader : public QObject
{
Q_OBJECT
public:
explicit FileDownloader(QUrl url, QObject *parent = 0, int number = 0);
QByteArray downloadedData() const;
void launchNewDownload(QUrl url);
QByteArray m_DownloadedData;
QNetworkReply* reply;
static QNetworkAccessManager *m_WebCtrl;
signals:
void downloaded();
private slots:
void fileDownloaded(QNetworkReply* pReply);
};
And here is the .cpp file :
QNetworkAccessManager* FileDownloader::m_WebCtrl = nullptr;
FileDownloader::FileDownloader(QUrl url, QObject *parent) :
QObject(parent)
{
if (m_WebCtrl == nullptr) {
m_WebCtrl = new QNetworkAccessManager(this);
}
connect(m_WebCtrl, SIGNAL (finished(QNetworkReply*)),this, SLOT (fileDownloaded(QNetworkReply*)));
launchNewDownload(url);
}
void FileDownloader::launchNewDownload(QUrl url) {
QNetworkRequest request(url);
this->reply = m_WebCtrl->get(request);
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
m_DownloadedData = pReply->readAll();
//emit a signal
pReply->deleteLater();
emit downloaded();
}
QByteArray FileDownloader::downloadedData() const {
return m_DownloadedData;
}
The issue is "QThread::start: Failed to create thread ()" when reaching about the 500th download. I tried to limit the number of downloads which run at the same time - but I always get the same issue. Besides, I tried to delete every downloader when finishing its task - it did nothing else than crashing the program ;)
I think that it is coming from the number of threads allowed for an only process, but I'm not able to solve it !
Does anyone have an idea that could help me ?
Thank you !
QNetworkAccessManager::finished signal is documented to be emitted whenever a pending network reply is finished.
This means that if the QNetworkAccessManager is used to run multiple requests at a time (and this is perfectly normal usage). finished signal will be emitted once for every request. Since you have a shared instance of QNetworkAccessManager between your FileDownloader objects, the finished signal gets emitted for every get call you have made. So, all the FileDownloader objects get a finished signal as soon as the first FileDownloader finishes downloading.
Instead of using QNetworkAccessManager::finished, you can use QNetworkReply::finished to avoid mixing up signals. Here is an example implementation:
#include <QtNetwork>
#include <QtWidgets>
class FileDownloader : public QObject
{
Q_OBJECT
//using constructor injection instead of a static QNetworkAccessManager pointer
//This allows to share the same QNetworkAccessManager
//object with other classes utilizing network access
QNetworkAccessManager* m_nam;
QNetworkReply* m_reply;
QByteArray m_downloadedData;
public:
explicit FileDownloader(QUrl imageUrl, QNetworkAccessManager* nam,
QObject* parent= nullptr)
:QObject(parent), m_nam(nam)
{
QNetworkRequest request(imageUrl);
m_reply = m_nam->get(request);
connect(m_reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded);
}
~FileDownloader() = default;
QByteArray downloadedData()const{return m_downloadedData;}
signals:
void downloaded();
private slots:
void fileDownloaded(){
m_downloadedData= m_reply->readAll();
m_reply->deleteLater();
emit downloaded();
}
};
//sample usage
int main(int argc, char* argv[]){
QApplication a(argc, argv);
QNetworkAccessManager nam;
FileDownloader fileDownloader(QUrl("http://i.imgur.com/Rt8fqpt.png"), &nam);
QLabel label;
label.setAlignment(Qt::AlignCenter);
label.setText("Downloading. . .");
label.setMinimumSize(640, 480);
label.show();
QObject::connect(&fileDownloader, &FileDownloader::downloaded, [&]{
QPixmap pixmap;
pixmap.loadFromData(fileDownloader.downloadedData());
label.setPixmap(pixmap);
});
return a.exec();
}
#include "main.moc"
If you are using this method to download large files, consider having a look at this question.
One solution could be to uses a QThreadPool. You simply feed it tasks (QRunnable) and it will handle the number of threads and the task queue for you.
However in your case this is not perfect because you will be limiting the number of simultaneous downloads to the number of threads created by QThreadPool (generally the number of CPU core you have).
To overcome this you will have to handle the QThread yourself and not use QThreadPool. You would create a small number of thread (see QThread::idealThreadCount()) and run multiple FileDownloader on each QThread.

How to properly stop QThread run() function?

I have one thread, producer and class consumer. Consumer is registered in QML, and producer is connected with signals to consumer. Producer send data to consumer, so consumer can update the model and GUI.
Code looks something like this:
Main function:
int main(int argc, char ** argv)
{
QGuiApplication app(argc, argv);
Producer producer;
Consumer consumer;
/* Connect signals between producer and consumer */
...
...
QQuickView view;
/* Set root context */
QQmlContext *ctxt = view.rootContext();
producer.start();
ctxt->setContextProperty("consumer", &consumer);
/*Connect signals between consumer and QML*/
...
view.show();
return app.exec();
}
Producer:
class Producer : public QThread
{
Q_OBJECT
protected:
void run()
{
while(true) {
if (someFlag == true)
{
// do some work
// emit signal with data to consumer
}
}
}
signals:
// Signals for sending data
};
The question is how to properly stop thread Producer when exit in application is pressed?
Instead of while(true) check for a "should end" condition, e.g. isInterruptionRequested().
Then, in main(), before returning, you tell the thread to stop, e.g. with QThread::requestInterruption and then wait on the thread
view.show();
const int ret = app.exec();
producer.requestInterruption();
producer.wait();
return ret;
I have a suggestion:
My way of doing is, Register the windows close event and do any sort of cleaning activities in it.
pseudo code below. try to fit it in your application.
Close filter event:
class CloseEventFilter : public QObject
{
Q_OBJECT
public:
CloseEventFilter(QObject *parent) : QObject(parent) {}
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::Close)
{
// Destroy your thread here.
producer->exit();//Make your thread available
//... may be global....producer is derived from QThread.
}
return QObject::eventFilter(obj, event)
}
}
};
Registering to windows
//Registering window
QGuiApplication *app = new QGuiApplication(argc,argv);
QWindow* qtwindow = new QWindow();
CloseEventFilter *closeFilter = new CloseEventFilter(window);
qtwindow >installEventFilter(closeFilter);
Why thread exit funtion (from documentation)
void QThread::exit(int returnCode = 0)
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.
By convention, a returnCode of 0 means success, any non-zero value indicates an error.
Note that unlike the C library function of the same name, this function does return to the caller -- it is event processing that stops.
No QEventLoops will be started anymore in this thread until QThread::exec() has been called again. If the eventloop in QThread::exec() is not running then the next call to QThread::exec() will also return immediately.
See also quit() and QEventLoop.
The while (true) if (someFlag) loop will peg the CPU at 100% when there's no work to be done. You don't want that!
Alas, there's no need to make the Producer be a QThread. It can be a plain old QObject. There are different ways of doing it.
You can use a zero-duration timer to keep the event loop active in the thread:
class Producer : public QObject
{
Q_OBJECT
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() == m_timer.timerId())
doWorkChunk();
}
void doWorkChunk() {
...
emit hasData(...);
}
public:
Producer(QObject * parent = nullptr) : QObject{parent} {
connect(this, &Producer::start, this, [this]{ m_timer.start(this, 0); });
connect(this, &Producer::stop, this, [this]{ m_timer.stop(); });
}
Q_SIGNAL void start();
Q_SIGNAL void stop();
Q_SIGNAL void hasData(const DataType &);
};
Instead of setting/clearing the flag, call start() and stop(). Then you can move the producer to any thread and it will work just fine, doing the right thing on exit (wrapping things up cleanly):
class Thread : public QThread {
using QThread::run;
public:
~Thread() { quit(); wait(); }
};
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
Producer producer;
Thread thread; // must come after producer above, the order has meaning!
producer.moveToThread(&thread);
thread.start();
return app.exec();
}
You can use the Qt Concurrent framework, too:
class Producer : public QObject {
{
Q_OBJECT
volatile bool m_active = false;
void doWorkChunk() {
...
emit hasData();
}
void doWork() {
while (m_active) doWorkChunk();
}
public:
using QObject::QObject;
~Producer() { stop(); }
void start() {
m_active = true;
QtConcurrent::run(this, &Producer::doWork);
}
void stop() {
m_active = false;
}
Q_SIGNAL void hasData(const DataType &);
};

Play QSoundEffect in QThread

I can not get the QSoundEffect to play in a separate thread. Could you please tell me why is the sound played only by the first code snippet and not by the second?
//main.cpp
#include <QCoreApplication>
#include "SoundThread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// #1
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
/* #2
SoundThread thread;
thread.start();
thread.wait();
*/
return a.exec();
}
and
//SoundThread.h
#ifndef SOUNDTHREAD_H
#define SOUNDTHREAD_H
#include <QThread>
#include <QtMultimedia/QSoundEffect>
class SoundThread : public QThread
{
Q_OBJECT
private:
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
while(true){}
}
};
#endif // SOUNDTHREAD_H
From the Qt documentation on QThread: -
By default, run() starts the event loop by calling exec()
Since you've inherited from QThread, you now have a run function which doesn't call exec(). Therefore, the event loop is not running and is most likely required for playing the sound effect.
Calling exec() should be substituted for the while(true){} as exec() will wait until exit() is called.
Doing it properly, with moving an object to the thread, based on "How to Really Truly Use QThreads..."
class Worker : public QObject
{
Q_OBJECT
public:
Worker();
~Worker();
public slots:
void PlaySoundEffect();
signals:
void finished();
void error(QString err);
private:
// store the sound effect, so we can reuse it multiple times
QSoundEffect* m_pAlarmSound;
private slots:
};
Worker::Worker()
{
m_pAlarmSound = new QSoundEffect;
m_pAlarmSound.setSource(QUrl::fromLocalFile(":/sound"));
}
Worker::~Worker()
{
delete m_pAlarmSound;
m_pAlarmSound = nullptr; // C++ 11
}
void Worker::PlaySoundEffect()
{
m_pAlarmSound->play();
}
// setup the worker and move it to another thread...
MainWindow::MainWindow
{
QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(PlaySoundEffect()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
// We can also connect a signal of an object in the main thread to the PlaySoundEffect slot
// Assuming MainWindow has declared a signal void Alert();
connect(this, &MainWindow::Alert, worker, &Worker::PlaySoundEffect);
// Then play the sound when we want: -
emit Alert();
}
While this seems like a lot of effort, there are many advantages of doing it this way. If, for example, you have a lot of sound effects, the method of inheriting QThread means that you're creating a thread per sound effect, which isn't ideal.
We could easily extend the above Worker object to hold a list of sound effects and play the one we want, by passing an enum into the PlaySoundEffect slot. As this thread is constantly running, playing sounds will incur less delay; it takes time and resources to create a thread at run-time.
You enter to an infinite loop at the end of your run function which causes to block the thread and consequently the QSoundEffect not working. It should be like:
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
exec();
}
Replace while(true){} by QThread::run();, which will launch an internal event loop and will wait (sleep) for events, such as timer events and asynchronous signals calling slots, which is probably what is happening internally in QSoundEffect: When you call QSoundEffect::play() Some event (probably signals/slots) are queued in the event queue from within QThread, but nothing is processing the event queue. Remember: you are overriding virtual void run() and the original implementation was calling QThread::exec() for you. It is always a good idea to always call your super classes virtual function whenever you override them, as long as they are not pure virtual.
void run()
{
QSoundEffect alarmSound;
alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
alarmSound.play();
QThread::run();
}
Some people suggested that calling QThread::exec() would do the trick. They may be right. I have to check the implementation of QThread to confirm that it is the only thing called in QThread::run() implementation. I personally think (by experience) that it is always safer to call your superclasse's virtual function in case there are other things called (other than QThread::exec() for this particular case).
Another option would be to move your QSoundEffect instance onto the thread and use signals and slots default auto-connection behaviour type to switch threads.
SoundPlayer.h:
#ifndef SOUNDPLAYER_H_
#define SOUNDPLAYER_H_
#include <QObject>
#include <QThread>
#include <QSoundEffect>
class SoundPlayer : public QObject
{
Q_OBJECT
public:
SoundPlayer();
signals:
void play();
private:
QThread m_thread;
QSoundEffect m_alarmSound;
};
#endif
SoundPlayer.cpp :
#include "SoundPlayer.h"
SoundPlayer()
{
m_alarmSound.setSource(QUrl::fromLocalFile(":/sound"));
m_alarmSound.moveToThread(&m_thread);
connect(this, SIGNAL(play()), &m_alarmSound, SLOT(play()));
m_thread.start(); // QThread::exec() will be called for you, making the thread wait for events
}
And then calling the play() signal would start playing in the correct thread.
SoundPlayer player;
emit player.play(); // m_alarmSound.play() will be called by m_thread

How to overcome the Qthread synchronization issue here?

I have a main thread that loads the web-page and displays it. I have another thread running, that will just print the debug message on to the console.However, I am seeing that on running the QT-Thread, the web-page is not getting loaded. I tried putting the loading of web-page on to the constructor of thread, but this is also not helping here.Here is the code.
class MyJavaScriptOperations : public QObject {
Q_OBJECT
public:
Q_INVOKABLE qint32 MultOfNumbers(int a, int b) {
qDebug() << a * b;
return (a*b);
}
};
#if 1
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
public:
void run();
};
MyThread::MyThread()
{
qDebug()<<"Constructor called";
QWebView *view = new QWebView();
view->resize(400, 500);
view->page()->mainFrame()->addToJavaScriptWindowObject("myoperations", new MyJavaScriptOperations);
view->load(QUrl("./shreyas.html"));
view->show();
this->run();
}
void MyThread::run()
{
qDebug()<<"Thread running";
while(1)
{
qDebug()<<"Fire Callback now";
}
}
#endif
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyThread t;
//t.run();
return a.exec();
}
Just because code is in a subclass of QThread doesn't mean that code is executed in that thread. Your main thread constructs the object, and that constructor calls run(). This means that the code of the run method is still executed in the main thread, and - as it is blocking - the line a.exec() is never called, and the main thread never gets an event loop which is required for paint events and the like.
What you need to do is start the thread and wait for run() being executed:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// TODO: Code for your web view goes here. You will probably need to
// pass the created web view into the constructor of MyThread
MyThread t;
// start the thread - this will put an event in the main event loop
t.start();
// start the event loop - this will lead to MyThread::run() being called
return a.exec();
}
This is enough to get your example running, but you will get errors when closing the web view as your way of using threads is not the intended one: If you want to make your code stable, put the code of your run() method in a separate worker class and use a default QThread for managing that, without subclassing QThread.
I recommend reading the Qt5 Documentation on threads, which is also applicable for earlier versions of Qt.

Resources