PDA

View Full Version : moveToThread and connecting Signals



Qiieha
13th August 2015, 15:39
Hello guys,

I want to figure out the difference between connecting signals of an object before moveToThread and connecting signals after moveToThread method.

Is it right that first assumption results in asynchron slot calls of mySlot and second results in synchron slot calls of mySlot?




QThread* thread = new QThread();
MyObject* object = new MyObject();

object->moveToThread(thread);

connect(object,SIGNAL(mySignal()),this,SLOT(mySlot ())); // asynchronous

thread->start();






QThread* thread = new QThread();
MyObject* object = new MyObject();


connect(object,SIGNAL(mySignal()),this,SLOT(mySlot ())); // synchronous

object->moveToThread(thread);


thread->start();

jefftee
13th August 2015, 16:15
What did your testing show?

Qiieha
13th August 2015, 16:34
My testing shows that behaviour, but I don't understand it.

anda_skoa
13th August 2015, 17:39
I want to figure out the difference between connecting signals of an object before moveToThread and connecting signals after moveToThread method.

Is it right that first assumption results in asynchron slot calls of mySlot and second results in synchron slot calls of mySlot?


There is no difference.
In both cases the connect does not specify a connection type, thus both implicitly use Qt::AutoConnection.
In both cases the sender and receiver objects live in different threads, so the resulting connection behavior is Qt::QueuedConnection, i.e. asynchronous.
in both cases you can force synchronous calls by specifying the connection type as Qt::DirectConnection

Cheers,
_

jefftee
14th August 2015, 00:22
anda_skoa, I certainly don't disagree with what you posted, but I wanted to make sure I understand correctly.

I thought that for signals/slots between different threads, you *must* used Qt::QueuedConnection (or default to Qt::AutoConnection) for the slot to be executed in the receiver thread. This is done asynchronously of course since the event is posted in the receiver's event loop.

Specifying Qt::DirectConnection may indeed result in the slot being invoked synchronously, but I believe that means that it will execute in the same thread as the sender, which is likely to cause problems if the slot uses member variables or executes other methods in the receiving class.

Is that correct or is my understanding incorrect?

Thanks

anda_skoa
14th August 2015, 08:07
I thought that for signals/slots between different threads, you *must* used Qt::QueuedConnection (or default to Qt::AutoConnection) for the slot to be executed in the receiver thread. This is done asynchronously of course since the event is posted in the receiver's event loop.

Exactly.



Specifying Qt::DirectConnection may indeed result in the slot being invoked synchronously, but I believe that means that it will execute in the same thread as the sender, which is likely to cause problems if the slot uses member variables or executes other methods in the receiving class.

Yes, also correct.
If you force a direct conncetion, then you will need explizit synchronization between threads.

To summarize:

- Qt::DirectConnection: like a direct method call. Slot called by the thread that executes "emit"
- Qt::QueuedConnection: always asynchronous/indirect call. Slot called by thread owning the receiver object.
- Qt::AutoConnection:
* if thread that executes "emit" is the receiver object's owner -> behaves like Qt::DirectConnection
* if thread that executes "emit" is not the receiver object's owner -> behaves like Qt::QueuedConnection

Qt::AutoConnection does "the right thing" for most use cases, so we only rarely see connect() with an explicit connection type argument.
But both specific options have their use cases.

Cheers,
_

Qiieha
14th August 2015, 08:14
I'm a bit confused, cause I also agree with that what you are posted, but the results of following test confuse me. Maybe I did something wrong. I know passing a pointer through signal is very bad practice, but
it's just within the meaning of a test.

In the attachements there's a small testApp. Maybe you could tell me, what I have to change, so that the app work properly.

Explanation:
In main.cpp I start some QRunnables via QThreadPool and wait for finishing.
In the QRunnable I create an Object and connect a signal to a slot of a singleton and emit the signal. The slot of the singleton should get called synchronously. I tried Qt::DirectConnection, etc... Until now I did not achieve the explected result.

And I don't think I need a QMutex at the slot of the singleton.



void NetworkAccessManager::doIt(MyObject* me)//slot of singleton, should get called synchronously
{
//QMutexLocker locker(&mutex);
qDebug() << this->thread()->currentThreadId();
qDebug() << "doIt" << me->getName();
qDebug() << "1";
qDebug() << "2";
qDebug() << "3";
qDebug() << "4";
qDebug() << "5";
me->emitFinished();
}

anda_skoa
14th August 2015, 09:22
In the QRunnable I create an Object and connect a signal to a slot of a singleton and emit the signal. The slot of the singleton should get called synchronously.

And it should.
Have you checked the thread id at the emit and in the slot?

Cheers,
_

Qiieha
14th August 2015, 09:32
If it's Qt::DirectConnection the ThreadID is (as expected and posted from you) the same. So how can I achieve that the slot is called serialized, if multiple runnables "call" the slot at the same time? QMutex is the solution, right?

If it's Qt::AutoConnection the slot never get called. Why?

anda_skoa
14th August 2015, 10:26
If it's Qt::DirectConnection the ThreadID is (as expected and posted from you) the same. So how can I achieve that the slot is called serialized, if multiple runnables "call" the slot at the same time? QMutex is the solution, right?

yes



If it's Qt::AutoConnection the slot never get called. Why?
The thread of the receiver object, in your case the main thread, is not running an event loop.
Qt::QueuedConnection behavior needs an event loop in the receiver thread, to process the "call slot asynchronously" event.

Cheers,
_

Qiieha
14th August 2015, 11:09
Ok thanks for the explanation!

Can I borrow your know-how from you for follwing assumption:

Supposed I have class MainObject. Class MainObject has a QThread* as member, which is initial set to 0.
If the thread is needed while execution it gets created and it's signal destroyed() is connected to a slot setThreadToNULL in MainClass, which set the member-pointer to 0.

In the destructor of main class the thread pointer is checked if it's a NULL-Pointer. If yes, the thread has completed. If no, the thread has not completed, and has to be terminated in destructor.

If the connection between them is direct, following could happen:




MainObject::~MainObject{

if(thread){

....// thread gets destroyed in the meantime

//here thread->terminate(); crashes

}
}



How can you achieve that this cannot happen? QPointer?

thanks

anda_skoa
14th August 2015, 11:48
You can use either locking with a mutex or use a QAtomicPointer with fetchAndStoreOrdered().

Also, never call QThread::terminate()

Cheers,
_

Qiieha
14th August 2015, 13:22
Ok, so if I decide to use QMutex, all I have to do is to guard the setThreadToNULL slot and the destructor.

The mutex will prevent(block) the thread from deleting, while destructor is executing. Right?

anda_skoa
14th August 2015, 14:57
Yes, but I don't really understand why you need to set the variable to null and not just always delete the thread at the end?

Cheers,
_

Qiieha
14th August 2015, 16:30
Cause I think deleting a QThread while executing is dangerous, isn't it?

If I don't finish the threading properly the app chrashes.



if(worker){//thats the pointer that is checked
worker->interrupt();//a custom method that finishes the run method in object Worker, which is moved to QThread
thread->quit();
if(!thread->wait(3000))
{
thread->terminate();
thread->wait();
}

}

anda_skoa
14th August 2015, 23:47
Cause I think deleting a QThread while executing is dangerous, isn't it?

Yes, but you can always do the same operation:
- you tell the thread to stop
- you wait for the thread to stop
- you delete the thread object

No need for the thread to "remove itself".

Cheers,
_

Qiieha
17th August 2015, 07:35
Ok, that's true.
Thank you for explanations and tips concerning threading behaviour, Connecting-Types, etc.
Your support is great.