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?
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.
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
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 :)
Related
I am trying to build a multithreaded server. The problem that I am encountering is that the emitted signal sends message to all clients instead to the specific one on a specific thread.
I tried to solve the issue by creating a QList of threads, but how do I connect them specifically?? When signal is emitted, it wakes up all my threads, the problem is getting worse by each connection, since the clients are dynamically allocated as they are connecting.
Code:
void NetworkServer::incomingConnection(qintptr socketDescriptor)
{
QThread *thread = new QThread;
threadhandle *session = new threadhandle;
QObject::connect(this, &NetworkServer::stopped, session, &threadhandle::close);
QObject::connect(this, &NetworkServer::stopped, thread, &threadhandle::quit);
QObject::connect(session, &threadhandle::closed, thread, &threadhandle::quit);
QObject::connect(thread, &QThread::started, this, &NetworkServer::threadStarted, Qt::DirectConnection);
QObject::connect(thread, &QThread::finished, this, &NetworkServer::threadFinished, Qt::DirectConnection);
QObject::connect(thread, &QThread::finished, session, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
//the problem is here, I can send message to all clients connected, but how to specifiy a thread/client???
QObject::connect(this, &NetworkServer::sendMsgToThread, session, &threadhandle::sendMsgToClient);
thread->start();
session->moveToThread(thread);
QMetaObject::invokeMethod(session, "open", Qt::QueuedConnection, Q_ARG(qintptr, socketDescriptor));
Update - 40 minutes later - Problem solved
I apologize for not providing more details, basically I am building a simple client-server relationship. Message is send from server GUI line in text format to a specific client.
After some thinking I came up with a solution.
so for each new thread, we will append its session to: QList<threadhandle*>sessionList
After that we are going to edit the sendMsgToClient()
// user clicks on Send Message button in GUI
void NetworkServer::sendMsgToClient(int clientNum, QString msg)
{
//connect the signal to a specific thread, clientNum is selected from a table in mainWindow GUI where the NetworkServer class is initiated
QObject::connect(this, &NetworkServer::sendMsgToThread, sessionList.at(clientNum), &threadhandle::sendMsgToClient);
//emit the signal to specific thread
emit sendMsgToThread(clientNum, msg);
//disconnect the signal, since it is no longer needed
QObject::disconnect(this, &NetworkServer::sendMsgToThread, sessionList.at(clientNum), &threadhandle::sendMsgToClient);
}
Hard to say really without MRE that builds and presents your exact problem. But, guessing from context: no clue how NetworkServer::sendMsgToThread or threadhandle::sendMsgToClient are implemented, but I guess you could store the thread ID/index/any other identifier and then pass it to specific thread only, possible via some proxy object?
Point is, sendMsgToThread gets connected to multiple slots, which is probably not what you want; instead those single session-thread pairs need to be somehow distinguished, e.g. by thread ID/its index in list, whatever.
I guess you could wrap the session and thread in some other class, that would be stored (and referred to) by the server by some of the aforementioned IDs, and it would forward the messages to the worker (session) object. But I cannot really tell without at least basic knowledge of your code's architecture.
So what I am trying to do is use Qt signals and slots to pass around an image through a smart_ptr so that it will delete itself when everything that needs to use the data is done accessing it.
Here is the code I have:
Class A, inherits QObject:
signals:
void newImageSent(boost::shared_ptr<namespace::ImageData> &image);
Class B, inherits QObject:
public slots:
void newImageRecieved(boost::shared_ptr<namespace::ImageData> &image)
{
// Manipulate Image
}
Connection Code:
QObject::connect(classAPtr.get(),
SIGNAL(newImageSent(boost::shared_ptr<namespace::ImageData>)),
classBPtr.get(),
SLOT(newImageRecieved(boost::shared_ptr<namespace::ImageData>)),
Qt::QueuedConnection);
When I try to do the connection is always returns false though, so is there something I am missing?
In a queued connection the signal is queued in the event loop and its parameters are copied.
Therefore the slot is not directly executed.
To make copying possible you have to register the type via qRegisterMetaType, see also here.
Since you are using shared pointers easiest solution would be to transmit them by value, then you would not have to bother with the references as Frank Osterfeld pointed out.
The way you create the connection is string based and as result is easy to get wrong, especially when namespaces are involved.
Using typedef would ease the pain a little and make it easier to spot errors.
For example you could do
typedef boost::shared_ptr<namespace::ImageData> ImageDataPtr;
and use ImageDataPtr from then on.
Especially on registering the type as meta type which you have to do since you are using a queued connection.
If you are using Qt5 then you can rely on the new connection syntax which ensures correctness during compilation as it does not rely on string comparisons:
QObject::connect(classAPtr.get(), &A::newImageSent,
classBPtr.get(), &B::newImageRecieved,
Qt::QueuedConnection);
I have a class, audio_engine_interface, and in main.cpp, I add it to the QML thing.
viewer.rootContext()->setContextProperty("engine", engine);
In audio_engine_interface, I have a audio_engine class, which is computationally intensive—it needs to run on its own thread.
void audio_engine_interface::play()
{
QThread thread;
thread.start();
engine->moveToThread(&thread);
engine->play(); // Will use 100% of CPU
}
However, when I do this, the whole QML thread locks up, meaning I can't pause (pretty important). Am I missing something?
EDIT:
This thread won't mess up anything or access objects from other places. However, it does have a pause function that will need to be called at some point. For what it's worth, the engine is doing pitch shifting.
This is a problem: -
Qthread thread;
Creating a QThread object like this is creating it on the stack. When the function ends, the object will go out of scope and delete the QThread object.
You need to dynamically allocate the object on the heap: -
QThread* thread = new QThread;
Then remember to delete the thread, or set it to delete itself: -
//Qt 5 connect syntax
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
You should also be aware of thread affinity (the thread which an object is running on). I suggest reading this article on how to use QThread properly.
You have so many problems.
when you move to thread your object must not have a parent
your thread object is local variable so it will day immediately when udio_engine_interface::play() end execution
you are invoking you engine->play(); method directly and this means that it will be executed in current thread.
moveToThread means that slots invked by signals connected using default 5th parameter (Qt::AutoConnection) will be queued in event loop of given thread.
The easiest way to fix it is use QtConcurrent:
void audio_engine_interface::play()
{
QtConcurrent::run(engine, &EngineClass::play);
}
Depending what your engine does you should make it thread safe (use mutex locks an so on), without details it is hard to tell, what exactly you should do.
I have 2 classes: one maintains some loop (at leas for 2-3 minutes; and is inherited from QObject) and another shows up a progress dialog (inherited from QDialog).
I want to start the loop as soon as the dialog is shown. My first solution was:
int DialogClass::exec()
{
QTimer::singleShot(0, LoopClassPointer, SLOT(start()));
return __super::exec();
}
There is a problem with throwing exceptions from slots. so I considered a possibility to make public slot start() just a public function. But now I don't know how to make it works well. Things like this:
int DialogClass::exec()
{
LoopClassPointer->start();
QApplication::processEvents();
return __super::exec();
}
don't help. The dialog doesn't appears.
Is there a common approach to this kind of situations?
some details, according to questions:
I have to work with system with its own styles, so we have a common approach in creating any dialogs: to inherit them from stytle class, which is inherited from QDialog.
my 'LoopClassPointer' is an exported class from separate dll (there is no UI support in it).
I have a 'start' button in main app, which connected with a slot, which creates progress dialog and 'LoopClassPointer'. at the moment I send 'LoopClassPointer' instance in the dialog and don't whant to make significant changes in the architecture.
Take a look at QtDemo->Concurrent Programming->Run function
e.g. in Qt 4.8: http://qt-project.org/doc/qt-4.8/qtconcurrent-runfunction.html
In this situation, I recommend you separate the logic of the loop from the dialog. Gui elements should always be kept separate.
It's great that your worker class is derived from QObject because that means you can start it running on a separate thread: -
QThread* m_pWorkerThread = new QThread;
Worker* m_pWorkerObject = new Worker; // assuming this object runs the loop mentioned
// Qt 5 connect syntax
connect(m_pWorkerThread, &QThread::started, m_pWorkerObject, &WorkerObject::start);
connect(m_pWorkerThread, &QThread::finished, m_pWorkerThread, &QThread::deleteThis);
m_pWorkerObject->moveToThread(m_pWorkerThread);
m_pWorkerThread->start();
If you're not familiar with using QThread, then start by reading this.
The only other thing you require is to periodically send signals from your worker object with progress of its work and connect that to a slot in the dialog, which updates its display of the progress.
I have a Phonon.MediaObject that is connected to a web source through Phonon.AudioOutput. Therefore, when running play(), the main GUI freezes until the player starts playing (5 seconds at worst case).
This could be fixed by moving the player, or at least it's web fetching task. I've read online about moveToThread() function, but it doesn't seem to work.
I tried making a thread out of it, but without luck: http://pastebin.com/1iXdQD8Y (written in PyQt)
Any ideas?
This will require a bit more coding on your side.
I don't know python all that well, but from looking at your code I think that one mistake you are making is to assume that your PhononThread class is living in it's own thread, but it's actually living in thread in which it has been created.
I wouldn't even try to move objects like MediaObject between threads. One way to do it would be to create your own QObject descendant, then in it's constructor create all objects that you will need to play music. You will also need to add slots to your class to access every phonon function you will need to call from main thread. REMEMBER to create this object WITHOUT parent object.
Then you need to move it to newly created QThread and connect all signals/slots between threads.
I don't use python, but here is pseudo-C++ outline of how it should look like:
class MyPlayer: public QObject{
Q_OBJECT
public:
AudioOutput* ao;
MediaObject* mo;
MyPlayer() : QObject(0) { // note no parent QObject instance
ao = new AudioOutput();
... // Create and connect all objects
}
public slots:
void setCurrentSource ( const MediaSource & source ){
mo->setCurrentSource(source);
}
// And other methods that are not slots already.
};
Then in your application you do:
MyPlayer* mp = new MyPlayer();
QThread* th = new QThread();
connect(th, SIGNAL(finished()), mp, SLOT(deleteLater()));
connect( mainThreadObj, SIGNAL(setPlayerSource ( const MediaSource & ) ), mp, SLOT(setPlayerSource ( const MediaSource & ) ) );
... // and other signals; note that methods that are signals already can be conected witout wrappers:
connect( mainThreadObj, SIGNAL(playerPlay() ), mp->mo, SLOT(play()) );
mp->moveToThread(th);
For stopping your thread, just connect signal from your main thread to th's quit() slot, and emit it when needed.
Subclass QThread, reimplement run() with the stuff you want to happen in your thread, and then create an instance of your thread and call start() on it.
Just be careful about when you connect to things in your thread or from your thread, because you don't want to do a direct or auto connection, you want to do a queued connection in most cases.
This link shows two ways to use QThread, one as I just described, and it links to another producer consumer example using moveToThread().
what is the correct way to implement a QThread... (example please...)
Hope that helps.
Have you tried QtConcurrent::run? It runs function in a separate thread.
http://qt-project.org/doc/qt-4.8/qtconcurrentrun.html#run
or check it here https://stackoverflow.com/search?q=QtConcurrent%3A%3Arun