PDA

View Full Version : Call function of object within Threads.



marcos.miranda
12th January 2017, 20:49
Hello everyone.

I'm studying about threads and would like some help.
Learn how to perform functions that are on an object that has been moved to a thread.
I have to pass parameters to the function and receive the return of it to be able to display in a text edit.

Oops: Forgetting that objects and threads are generated by a "FOR".

first......: How to pass parameters to the function ???
second.: How to receive the return of it to be able to display in a text edit ???

I tried this:



void MainWindow::on_cmdIniciar_clicked()
{
// ARRAY DE THREADS
QThread *vthrThread[vnumCont];

// ARRAY DE OBJETOS
clsTask *vobjTask[vnumCont];


for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vobjTask[vnumIni] = new clsTask();
vthrThread[vnumIni] = new QThread();

//ERRO HERE
//QObject::connect(&vthrThread[vnumIni], SIGNAL(started), vobjTask[vnumIni], SLOT(fcMensagemTexto(QString::number(vnumIni))));

vobjTask[vnumIni]->moveToThread(vthrThread[vnumIni]);

vthrThread[vnumIni]->start();


}




QObject::connect(&vthrThread[vnumIni], &QThread::started, &vobjTask[vnumIni], &clsTask::fcMensagemTexto(QString::number(vnumIni) ));

ERRO:
cannot call member function 'QString clsTask::fcMensagemTexto(QString)' without object
QObject::connect(&vthrThread[vnumIni], &QThread::started, &vobjTask[vnumIni], &clsTask::fcMensagemTexto(QString::number(vnumIni) ));

COMPILER OUTPUT
error: cannot call member function 'QString clsTask::fcMensagemTexto(QString)' without object
QObject::connect(&vthrThread[vnumIni], &QThread::started, &vobjTask[vnumIni], &clsTask::fcMensagemTexto(QString::number(vnumIni) ));

anda_skoa
13th January 2017, 08:56
If the thread has a single function and only executes it once, just pass the arguments to the class constructor.

You can also use QMetaObject::invokeMethod() with connection type Qt::QueuedConnection to invoke a slot on the worker object in the worker thread's context.

For the result you either need a signal on the worker object or use the invokeMethod() option the other way around on an object that belongs to the main thread.

Cheers,
_

marcos.miranda
13th January 2017, 13:22
Hi, anda_skoa.
First of all, thank you for answering me.

Before reading your considerations, I was modifying the project to simplify it and thus better understand how to work with threads and why it is giving error.

You could take a look to see where I'm going wrong, the project was like this.

Thank you for your attention.



// Arquivo .h
#include <QObject>
#include <QThread>
class clsThrTask : public QThread
{
Q_OBJECT
public:
clsThrTask(QString pstrIdThread, QObject *parent = nullptr); // Constructor with paramenter
void run() override;
signals:
void sigRetorno(QString);




//Arquivo .cpp
#include "clsthrtask.h"
clsThrTask::clsThrTask(QString pstrIdThread, QObject *parent) : QThread(parent)
{
vstrForRef = pstrIdThread;
}
void clsThrTask::run()
{
QString vstrResposta = "Threads nº: " + QString::number((long long) QThread::currentThreadId(),16) + " | FOR REF nº: " + vstrForRef;
// EMITE UM SIGNAL WITH DATA
emit sigRetorno(vstrResposta);
}




//Arquivo MainWindow.h
#include "clsthrtask.h"
namespace Ui {
class MainWindow;
}
class clsThrTask;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();




//Arquivo MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}

// ARRAY OF THREADS
QThread *vthrThread[vnumCont];

for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni] = new QThread(QString::number(vnumIni)); // Erro here.
QObject::connect(vthrThread[vnumIni], &clsThrTask::sigRetorno, ui->txtResultado, &QTextEdit::append); // Erro here.
vthrThread[vnumIni]->start();
}

1) error: no matching function for call to 'QThread::QThread(QString&)' vthrThread[vnumIni] = new QThread(vstrIdThread);

2) error: invalid conversion from 'QThread*' to 'const Object* {aka const clsThrTask*}' [-fpermissive]
QObject::connect(vthrThread[vnumIni], &clsThrTask::sigRetorno, ui->txtResultado, &QTextEdit::append);
^

marcos.miranda
13th January 2017, 15:40
Hi everyone

I found out where I was wrong, I'm talking about the first mistake.

VthrThread [vnumIni] = new QThread (vstrIdFor, this); Erro here.

VthrThread [vnumIni] = new clsMultiThr (vstrIdFor, this); Correct here.

Can someone help me in the second error.
Someone ???

2) Second erro is :


QObject::connect(vthrThread[vnumIni], &clsMultiThr::sigRetorno, ui->txtResposta, &QTextEdit::append);

error: invalid conversion from 'QThread*' to 'const Object* {aka const clsMultiThr*}' [-fpermissive]
QObject::connect(vthrThread[vnumIni], &clsMultiThr::sigRetorno, ui->txtResposta, &QTextEdit::append);
^


How would this connection work?


The neurons are dying and the blood is already in the shins. :D :D :D

Thank you for your attention.

anda_skoa
14th January 2017, 11:10
Your array needs to be of the subtype as well.

In general you might want to prefer a container, e.g. a vector, over a C array.

Cheers,
_

marcos.miranda
14th January 2017, 14:28
Hi, Anda Skoa.

I will have to convert an array of threads to an array of objects
I'm not undestend, there is how to you show this in code for me to understand.

Thanks..

d_stranz
14th January 2017, 20:43
This:


// ARRAY OF THREADS
QThread *vthrThread[vnumCont];

for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni] = new QThread(QString::number(vnumIni)); // Erro here.
QObject::connect(vthrThread[vnumIni], &clsThrTask::sigRetorno, ui->txtResultado, &QTextEdit::append); // Erro here.
vthrThread[vnumIni]->start();
}


needs to be this:



// ARRAY OF THREADS
clsThrTask *vthrThread[vnumCont];

for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni] = new clsThrTask(QString::number(vnumIni));
QObject::connect(vthrThread[vnumIni], &clsThrTask::sigRetorno, ui->txtResultado, &QTextEdit::append);
vthrThread[vnumIni]->start();
}


As anda_skoa said, your array of threads need to be of the same type as your derived class. The compiler is complaining because QThread does not have a signal named "sigRetorno".

Note that QTextEdit::append() is probably not an atomic method (that is, appending text is not protected by a mutex), so you could have two or more threads trying to modify the text edit at the same time. This could lead to memory corruption, a crash, or you might get lucky and it works just fine sometimes.

You would be better off connecting to a slot in your own class that protects the append() method from simultaneous access using a mutex.

marcos.miranda
15th January 2017, 00:18
Hi, d_stranz's.
I understood what you said, and it showed in code.
I tested it and it worked now.

Thank you very much for your explanation about the problem.
I will be making the project available on the site for other QT beginners,
To understand better about Threads.

Just to finalize this case study, I noticed that through Applications Output the following error message.



QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)


How do I resolve this?
Could you tell me what I should change in the code.

Thanks.

anda_skoa
15th January 2017, 10:29
Note that QTextEdit::append() is probably not an atomic method (that is, appending text is not protected by a mutex), so you could have two or more threads trying to modify the text edit at the same time.

No problem in this case, as the signal/slot connection between two threads has the slot called in the context of the receiver object's thread.
So append() is always called by the same thread, in this case the main thread.





QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QProcess(0x239e2e0), parent's thread is QThread(0x1e16850), current thread is clsThrTask(0x239e300)


You are probably passing "this" as the parent of an object that you create in "run()"

Edit: ah, no, you are creating the QProcess during class initialization, making it an object run by the main thread. You need to create it in run() or sltProcPing().
You should also be aware that sltPrintProcOut() is currently called by the main thread. If you want it called in the worker thread, you'll need to use a Qt::DirectConnection

Cheers,
_

marcos.miranda
15th January 2017, 20:28
Hi, Anda Skoa.

I modified
QProcess * vprocPing = new QProcess (); Who was in (* .h) to

QProcess * vprocPing and put
vprocPing = new QProcess (); In sltProcPing as you recommended. It did right stopped the messages in Application Output.
Thanks, Thanks and Thanks.

1ª) question
When you said that sltPrintProcOut () is being called by the main thread and if I want it called in the worker thread, I'll need to use a "Qt:: DirectConnection", it was for I change the



connect (vprocPing, SIGNAL(readyReadStandardOutput()), this, SLOT(sltPrintProcOut()));
to
connect (vprocPing, SIGNAL(readyReadStandardOutput()), this, SLOT(sltPrintProcOut()),Qt::DirectConnection);
???


2ª) question
This would not cause problems with the Append method of the Response TextEdit, since some threads could call the method in question at the same time ???

anda_skoa
15th January 2017, 21:07
1ª) question
When you said that sltPrintProcOut () is being called by the main thread and if I want it called in the worker thread, I'll need to use a "Qt:: DirectConnection", it was for I change the



connect (vprocPing, SIGNAL(readyReadStandardOutput()), this, SLOT(sltPrintProcOut()));
to
connect (vprocPing, SIGNAL(readyReadStandardOutput()), this, SLOT(sltPrintProcOut()),Qt::DirectConnection);
???


Yes, correct.



2ª) question
This would not cause problems with the Append method of the Response TextEdit, since some threads could call the method in question at the same time ???

No, that is still fine. The connection between the thread object and the text edit is a Qt::AutoConnection, so it detects that sender and receiver are two different threads.

Cheers,
_

marcos.miranda
16th January 2017, 11:44
Hi, Anda Skoa and everyone.

How do I set the "vthrThread" thread array, defined by the real-time user, to go out of the local scope to its method and stay in the global scope of the class in question?

It works like this.



void MainWindow::on_cmdIniciar_clicked()
{

Int vnumCont = ui->cboNumVezes->currentText().toInt();

clsThrTask *vthrThread[vnumCont];


for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni] = new clsThrTask(vstrIdThread,this);

QObject::connect(vthrThread[vnumIni], SIGNAL(sigRetorno(QString)),ui->txtResultado,SLOT(append(QString)));
....


I have tried it and it does not work.






(*.h)

Private:

Int vnumCont;
clsThrTask *vthrThread[];

(*.cpp)


void MainWindow::on_cmdIniciar_clicked()
{

for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni] = new clsThrTask(vstrIdThread,this);

QObject::connect(vthrThread[vnumIni], SIGNAL(sigRetorno(QString)),ui->txtResultado,SLOT(append(QString))); // Erro Here "SIGSEGV"
....


void MainWindow::on_cmdParar_clicked()
{

for(int vnumIni=0; vnumIni < vnumCont; vnumIni++)
{
vthrThread[vnumIni].terminate();


Could you show me the correct form please?


Oops: Sorry for my lack of education, Thanks for the previous explanation.
Thanks.

anda_skoa
16th January 2017, 12:40
See the suggestion to use a container, e.g. a vector/QVector

An array member would need to be a pointer to pointers, less obvious.

And don't call QThread::terminate(), that is almost always a bad idea.
Since your thread payload is event loop based, calling quit() should work.

Cheers,
_

marcos.miranda
16th January 2017, 16:46
Hi, Anda Skoa and everyone

On the Terminate () method, thanks for alerting me I took a look at QT Documentation.

Now on QVector, I'm reviewing the QT Documentation but I have no idea how to declare this class and start its constructor.

For simple examples I understood how to use it.

Any code examples to help?

Thanks.


Added after 16 minutes:

And before anyone says "You need to go back to your C ++ books and learn about variables and scoping. Your problem has nothing to do with everything and with basic C ++." I'm already doing this. :):):) :confused:

marcos.miranda
17th January 2017, 02:02
Hi, everyone.

Thanks, d_stranz.

I made the following change.



In (* .h) I will declare.

QVector <clsThrTask> * vthrThread;

In (* .cpp) I did:

User informs the amount of threads through "vnumCont".
VthrThread-> resize (vnumCont);

And how will you stay here?


For (int vnumIni = 0; vnumIni <vnumCont; vnumIni ++)
{

QString vstrIdThread = QString :: number (vnumIni); - // OK

VthrThread [vnumIni] = new clsThrTask (vstrIdThread, this); // ???? Erro Here.
QObject :: connect (vthrThread [vnumIni], SIGNAL (sigRetorno (QString)), ui-> txtResult, SLOT (append (QString)); // ???? Erro Here.



Someone to help with the code ??

Thanks.

anda_skoa
17th January 2017, 10:13
Almost



QVector<clsThrTask*> vthrThread;

A vector to pointers of your thread class

Cheers,
_

marcos.miranda
17th January 2017, 12:25
Hello everyone.

I hope this little QT project on parallel processing will be the starting foot tip for many like me, beginners in C ++ and QT Framework.

In this project we use some concepts about Threads (QThread), Processes (QProcess) and Vectors (QVector).

I want to register a special thanks to Anda Skoa, D_Stranz and Edr567, without them it would not be possible to end this case study.

I hope to count on you some other time.

Thanks, Thanks and Thanks.

The project will be available for download at the end of the post. :D:D