PDA

View Full Version : QThread Proper Use ?? connect Problems....



GuiHanoi
23rd February 2016, 10:23
Hi everyone,

I'm new on this forum. I'm using Qt4.8.6 for its GUI performances but now I want to use QThreads properly. I saw on the web different ways to declare and use them.

- redefinition of methods run() of Object inheriting of QThread class => Wrong way

- definition of a Worker and worker->moveToThread (QThread*) => Right way ??? (https://forum.qt.io/topic/14378/my-tutorial-on-how-to-properly-use-qthreads)

I developed a small project with these two methods to compare them. threadA which displays "A" and threadB which displays "B". To start/stop each threads, there are buttons. Both of these methods work (displaying AAAAA when AButton pushed, BBBBBBB when BButton pushed and ABABABABABA when both are pushed...

BUT if I follow steps of the second method (Correct use), I must connect :


connect(thread, SIGNAL(started()), worker, SLOT(process())); OK
connect(worker, SIGNAL(finished()), thread, SLOT(quit())); OK

but when I add these two connections, I have : Violation of Ox0FFFFFFFF emplacement Mutex....


connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

How can I do to make this error disappear ?
Other question, in my code when i'm displaying threadID, it always displays 00000000159 (for example), for both threads ! I think I'm missing something.... Thread must NOT have the same ID ???? principles ??? (same problem in the two methods)

this is my code, without hpp files :
-------------------------------------------
Worker.cpp


Worker::Worker() { stopped = false;} // attribute of Worker instance

void Worker::process()
{
while (!stopped)
std::cout << messageStr.toStdString(); // attribute of Worker instance
emit finished();
}

void Worker::stop() { stopped = true;}
void Worker::start() {stopped = false;}

---------------------------------------------
ThreadDialog.cpp


ThreadDialog::ThreadDialog(QWidget *parent) : QDialog(parent)
{
workerA = new Worker();
workerB = new Worker();

threadAButton = new QPushButton(tr("Start A"));
threadBButton = new QPushButton(tr("Start B"));
quitButton = new QPushButton(tr("Quit"));
quitButton->setDefault(true);

threadA = new QThread;
workerA->setMessage("A ");
workerA->moveToThread(threadA);

threadB = new QThread;
workerB->setMessage("B ");
workerB->moveToThread(threadB);

connect(threadA, SIGNAL(started()), workerA, SLOT(process()));
connect(workerA, SIGNAL(finished()), threadA, SLOT(quit()));
//connect(workerA, SIGNAL(finished()), workerA, SLOT(deleteLater()));
//connect(threadA, SIGNAL(finished()), threadA, SLOT(deleteLater()));

connect(threadB, SIGNAL(started()), workerB, SLOT(process()));
connect(workerB, SIGNAL(finished()), threadB, SLOT(quit()));
//connect(workerB, SIGNAL(finished()), workerB, SLOT(deleteLater()));
//connect(threadB, SIGNAL(finished()), threadB, SLOT(deleteLater()));

connect(threadAButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadA()));
connect(threadBButton, SIGNAL(clicked()), this, SLOT(startOrStopThreadB()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
.... // displayLayout
}

void ThreadDialog::startOrStopThreadA()
{
if (threadA->isRunning())
{
workerA->stop();
threadAButton->setText(tr("Start A"));
}
else
{
std::cout << threadA->currentThreadId() << std::endl;
workerA->start();
threadA->start();
threadAButton->setText(tr("Stop A"));
}
}

void ThreadDialog::startOrStopThreadB()
{
if (threadB->isRunning())
{
workerB->stop();
threadBButton->setText(tr("Start B"));
}
else
{
std::cout << threadB->currentThreadId() << std::endl;
workerB->start();
threadB->start();
threadBButton->setText(tr("Stop B"));
}
}

void ThreadDialog::closeEvent(QCloseEvent *event)
{
workerA->stop();
workerB->stop();
threadA->wait();
threadB->wait();
threadA->exit();
threadB->exit();

event->accept();
}

Please Help me to understand QThread features... :(
Thank you

GuiHanoi

anda_skoa
23rd February 2016, 10:40
- redefinition of methods run() of Object inheriting of QThread class => Wrong way

- definition of a Worker and worker->moveToThread (QThread*) => Right way ??? (https://forum.qt.io/topic/14378/my-tutorial-on-how-to-properly-use-qthreads)

Both options are OK, it depends on what the thread does.
The first is the better option for something that is a single, long operation, the second is a good option for things that need events while processing.



I developed a small project with these two methods to compare them. threadA which displays "A" and threadB which displays "B". To start/stop each threads, there are buttons. Both of these methods work (displaying AAAAA when AButton pushed, BBBBBBB when BButton pushed and ABABABABABA when both are pushed...

I personally would chose option 1 for that, way easier IMHO.



but when I add these two connections, I have : Violation of Ox0FFFFFFFF emplacement Mutex....


connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));


At which of the two connects to you get the error?
It looks fine.



Other question, in my code when i'm displaying threadID, it always displays 00000000159 (for example), for both threads ! I think I'm missing something.... Thread must NOT have the same ID ???? principles ??? (same problem in the two methods)

Your worker code does not output any thread ID, you only ever write the main thread's ID in the dialog code.


Worker.cpp


Worker::Worker() { stopped = false;} // attribute of Worker instance

void Worker::process()
{
while (!stopped)
std::cout << messageStr.toStdString(); // attribute of Worker instance
emit finished();
}

void Worker::stop() { stopped = true;}
void Worker::start() {stopped = false;}


Concurrent access to members by multiple threads needs to be serialized, e.g. by using a mutex.
Here that is the access to "stopped", which is accessed by the worker thread in process() and by the main thread in stop() and start().





void ThreadDialog::closeEvent(QCloseEvent *event)
{
workerA->stop();
workerB->stop();
threadA->wait();
threadB->wait();
threadA->exit();
threadB->exit();

event->accept();
}

QThread::exit() doesn't make much sense here. When wait() returns the thread has already exited.

Cheers,
_

GuiHanoi
24th February 2016, 09:58
Hi,

Thank you for your fast but helpful answer !

In my case, I have to display camera data stream (for visualization) into a QLabel but still use GUI (buttons or process, etc.). So I heard you well, I have to choose the second option. (Currently, I had implemented the first method in my solution, but I noticed that it has low performance (200 to 300ms refreshing cycle however camera has only 50ms exposure time...) but didn't crash. So I thought that I was mistaken somewhere.

if I only write : connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater());
When I click on start button, it displays A correctly, and when I click on Stop button, it stops displaying A. OK Correct functionality
But when I click one again on start button, I have an Exception HeapAlloc (malloc.c).

if I only write : connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater());
Correct functionality until I click once again on start button : Ecxeption 5DB3CCAE (QtCored4.dll), emplacement Violation (qmutex.cpp) QMutex::lock()

If I'm writing these two lines : same error QMutex::lock()

I thought that because of my TWO Worker declarations, I didn't have to put Mutex protection. Why do I have to do that ? because "stopped" member is not shared with threadA and B because there are two Workers... ??

So I wrote QThread::currentThreadId() in Worker process method and Yes I have now several ID numbers of thread, but each time a different. :s I thought that there were my two threads that were doing the action and so I had thought I would only see two ID numbers.. maybe because of this line : connect(worker, SIGNAL(finished()), thread, SLOT(quit()) ????

Ok for the dispensable exit() !

Thank you
Thanks

anda_skoa
24th February 2016, 10:16
In my case, I have to display camera data stream (for visualization) into a QLabel but still use GUI (buttons or process, etc.). So I heard you well, I have to choose the second option.

If you like that option better, go for it.
I would be using option one.



if I only write : connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater());
When I click on start button, it displays A correctly, and when I click on Stop button, it stops displaying A. OK Correct functionality
But when I click one again on start button, I have an Exception HeapAlloc (malloc.c).

Ah, so the crash does not happen at connect. That was puzzling me.
That you crash on second start() is understandable, you are calling a method on an invalid pointer (the object got delete).



I thought that because of my TWO Worker declarations, I didn't have to put Mutex protection. Why do I have to do that ? because "stopped" member is not shared with threadA and B because there are two Workers... ??

The member is accessed by the respective worker thread and the main thread.
Alternatively to implementing that correctly with a mutex, you could also use QThread::isInterruptionRequested() in the loop and QThread::requestInterruption() instead of stop().



So I wrote QThread::currentThreadId() in Worker process method and Yes I have now several ID numbers of thread, but each time a different. :s I thought that there were my two threads that were doing the action and so I had thought I would only see two ID numbers.. maybe because of this line : connect(worker, SIGNAL(finished()), thread, SLOT(quit()) ????

When you call QThread::start() it will get a new thread from the operating system at let it handle run(), which in your case just emits started() and runs process().

Cheers,
_

GuiHanoi
1st March 2016, 12:39
Thank you for your answers,

now I understand better QThread class and its functionality. last question. Is this a problem not to call deleteLater() for a thread ?
Thanks

anda_skoa
1st March 2016, 13:37
If your thread exists for the whole life time of the program, then you don't necessarily have to manually delete it.

deleteLater() is used here only to remove it once it has finished, but you could also ask it to stop, wait for it to be stopped and then delete it with "delete" or let its QObject parent delete it (as long as you have stopped it before).

Cheers,
_