Reputation: 6181
I'm working on an app which does some TCP communication, uses a database and has a GUI (pretty common). While experimenting with a database across the internet I've noticed slow responses of the GUI, which motivated using threads to handle the back end. I'm playing with this now and considering a full app re-design to adequately tackle the issue. So I'm imagining to separate GUI (a class derived from QMainWindow) and back end stuff (a domain class, derived from QObject). This domain class looks like (I say so because I'm clearly not an authority on patterns - always learning) a facade pattern. The idea is to construct both objects in the main function and then pass a pointer of DomainClass to MainWindow, i.e. MainWindow(DomainClass *domain).
Then comes the very point of this question. I'm imaging to construct many (not so much actually) objects in the Domain an make them communicate via signal/slots mechanics. Like the following:
QThread* threadDB = new QThread;
m_database = new Database;
m_database->moveToThread(threadDB);
threadDB->start();
QThread* threadTM = new QThread;
m_tm = new TM;
connect(m_database, &Database::dbConnected, m_tm, &TM::onDbConnected);
m_tm->moveToThread(threadTM);
connect(threadTM, &QThread::started, m_tm, &TM::init);
threadTM->start();
But I'm getting the following:
QObject::connect: Cannot queue arguments of type 'QSqlDatabase'
(Make sure 'QSqlDatabase' is registered using qRegisterMetaType().)
I've noted that it works fine if I leave m_tm out of the thread. Like the following:
QThread* threadDB = new QThread;
m_database = new Database;
m_database->moveToThread(threadDB);
threadDB->start();
m_tm = new TM;
connect(m_database, &Database::dbConnected, m_tm, &TM::onDbConnected);
Is this design reasonable? Which alternatives might be considered?
Upvotes: 0
Views: 164
Reputation: 6181
In respect to @Kuba Ober comments...
Moved thread management job to domain's owner, so it does:
QThread *thread = new QThread;
domain->moveToThread(thread);
thread->start();
Changed Domain's constructor to do:
QTimer::singleShot(0, this, SLOT(init()));
Works pretty nice :)
Upvotes: 0
Reputation: 6181
I'm making this an answer to have more room, but it might be tough as a comment considering @Kuba Ober answer.
To solve the second issue (pointed by @Kuba Ober) may I move DomainClass entirely to another thread? Coding the following on the constructor:
QThread* thread = new QThread;
this->moveToThread(thread);
connect(thread, &QThread::started, this, &DomainClass::init);
thread->start();
and coding the following on init():
m_database = new Database;
m_tm = new TM;
connect(m_database, &Database::dbConnected, m_tm, &TM::onDbConnecte
Upvotes: 0
Reputation: 98505
There are two issues. First, the runtime error tells you exactly what's wrong. You're trying to pass a database instance by value. Pass it by a pointer instead. The Qt metatype system knows how to handle such pointers.
class Database {
QSqlDatabase m_db;
...
public:
Q_SIGNAL void dbConnected(QSqlDatabase*);
...
};
Secondly, you can only use a QSqlDatabase
instance from the thread it was created in (doc). So there's no point to passing it to an object living in a different thread. You should put TM
in the same thread as that of the Database
object, or you should have the Database
object further encapsulate the database, exposing only a signal-slot interface that can then be used from any thread by using thread-safe queued calls.
Finally, you shouldn't use a "one thread per object" pattern. You must be able to give a reason, supported by measurements, that it is useful/helpful. A proliferation of threads is a bad thing - you should never have more than you have cores.
Upvotes: 1