[Qt-interest] Qt::AutoConnection fails

Oliver.Knoll at comit.ch Oliver.Knoll at comit.ch
Tue Apr 14 11:25:48 CEST 2009


Stefan Bisplinghoff wrote on Tuesday, April 14, 2009 10:42 AM:

> ...
> What I understand is, that connect uses Qt::QueuedConnection if the
> signal has been sent from another thread, otherwise
> Qt::DirectConnection is used.

First off, it is important to realise that Qt::AutoConnect decides at *connection time* whether Qt::QueuedConnection or Qt::DirectConnection has to be used. It does this by comparing the threads (http://doc.trolltech.com/4.5/qobject.html#thread) to which the QObject based object is associated with, at the point where connect() is called. If both objects reside in the same thread, then Qt::DirectConnection will be used, otherwise Qt::QueuedConnection.

Or put in other words: once this connection type decision has been made by Qt this won't change, regardless of whether the signals are emitted from another thread later on!

Applied to your example:

> 	SomeComponent( Database* theDatabase ) : m_database(theDatabase)
> 	{
> 		// Qt::AutoConnection is used by default!
> 		connect(m_database,
> 			SIGNAL(dataChanged()),
> 			this,
> 			SLOT(someAction()))
> 	}

this yields a Qt::DirectConnection, since both the Database ("m_database") and SomeComponent ("this") are associated to the same (main) thread when connect() is called.

> As shown in the example below, my emit
> is executed inside the second thread, but initiated by the database
> instance, which lives in the main thread. Is this the reason, why the
> AutoConnection gets confused?

I think the "database instance lives in the main thread" gives the confusion here. Again, it is important to understand *who exactly* emits the signal:

Your worker thread calls modifyDatabase() on the m_database instance:

> 	void run()
> 	{
> 		forever {
> 			aquire_some_data();
> 			m_database->modifyDatabase();

which on its turns simply does a:

> 	void modifyDatabase()
> 	{
> 		emit dataChanged();
> 	}

that is, the signal 'dataChanged()' is sent from the *worker thread*, and *not* the main thread, even though you say "it lives in the main thread". Yes, the database is associated with the main thread (QObject::thread() would return the main thread), but the "emit dataChanged()" is called within the context of your worker ("Hardware") thread - and only that is which counts!

Summary: we have a Qt::DirectConnection between the m_database and the SomeComponent, but the signals are sent from the "Hardware" (worker) thread. This is potentially dangerous, if you don't do any synchronisation (QMutex, ...) yourself. And especially doing widget (GUI) updates will most probably fail (only the "main" thread is allowed to update the GUI).

So what you really want here is a *queued* connection, so the signals as sent by the worker thread are automagically synchronised by the Qt event queue and your slot in the SomeComponent is executed in the context of the "main" thread.

Especially when dealing with threads it is good practise to explicitly set the connection type - don't rely on the AutoConnect feature! It is errorprone, as shown in the example above!

To be honest I don't know exactly what goes wrong in your code, i.e. what you mean by "emitted signals triggered by database changes in the other thread are not registered": does that mean the slot is not called at all?

Note that in your given example I think the slot *should* be called, but in the context of the "worker" thread! So maybe in your debugger you were expecting something to happen in your main thread (which would not)?

Anyway, the situation you have above needs to be fixed - maybe that also solves your concrete problem :)


Cheers, Oliver
-- 
Oliver Knoll
Dipl. Informatik-Ing. ETH
COMIT AG - ++41 79 520 95 22



More information about the Qt-interest-old mailing list