Solved QThread + Shared Library
-
I'm studing
QThread
and I already wrote some simple examples, but I'm having problems to use the same logic to my real application.Today I have a C++ Qt 5 GUI Main Application that loads a collection of "plugins" (
shared libraries
) in run-time.
This plugins were written in C++ Qt too.All plugins are written in with the same C++ structure:
- Plugin_API.h (Interface - Virtual methods)
- Plugin.h (Just overwrite the Interface methods)
- Plugin.cpp (Implementation)
Here is the
Plugin_API.h
#ifndef PLUGIN_API_H #define PLUGIN_API_H #include <QObject> #include <QString> #include <QThread> #include <QDebug> class Plugin_API : public QObject { Q_OBJECT private: public: Plugin_API() { } virtual ~Plugin_API() = default; virtual void run() = 0; // ===== SIGNALS ===== // signals: void printConsole(const std::string msg); }; // Declare our interface: Q_DECLARE_INTERFACE(Plugin_API, "com.rotortest.plugin") #endif // PLUGIN_API_H
Here is the
Plugin.h
#ifndef PLUGIN_H #define PLUGIN_H #include <QObject> #include <QtPlugin> #include <iostream> #include "plugin_global.h" #include "plugin_api.h" class PLUGIN_EXPORT Plugin: public Plugin_API { Q_OBJECT Q_PLUGIN_METADATA(IID "com.rotortest.plugin") Q_INTERFACES(Plugin_API) public: explicit Plugin(QObject* parent = nullptr); ~Plugin() override; void run() override; private: public slots: }; #endif // PLUGIN_H
I load each plugin (
file
) in aforeach
loop using:QPluginLoader loader(file); if (loader.load()) { Plugin_API* plugin = qobject_cast<Plugin_API*>(loader.instance()); // Another stuff... }
Finally, after the Main Application load all plugins using
QPluginLoader
class, I want to run each plugin in a serial order, like this:std::vector<Plugin_API*> pluginStack; // Fill this vector above... for(size_t i = 0; i < pluginStack.size(); i++) { pluginStack[i]->run(); }
Now, I can to it just calling the
plugin::run()
method, but this method takes a long time to run.So, I think that will be better to change this code to support
QThread
to be able to:- Unlock the main application GUI
- Monitor the plugin status in another thread
I read the Qt 5 documentation link here about the 2 possibilities of
QThread
implementation:- Worker (I can't see how to do it in my case...)
- Extending the
QThread
class (I think that is the most close with my problem...but I don't have sure...)
I did both implementations in a simple example, and it works. But I need some help to implement it in my real case above.
Could you help me to move this implementation above to a new implementation where:"When the main application calls the
plugin::run()
method, it will be started in a new thread....and when therun()
method finish, this new thread is completlyfinished
andquit
?"
Another Youtube tutorial reference:
- Extending the
QThread
example: https://www.youtube.com/watch?v=LCIes6xehlA&list=PL2D1942A4688E9D63&index=33 - Worker example: https://www.youtube.com/watch?v=yazMHbIew0Q&list=PL2D1942A4688E9D63&index=35
-
You are welcome!
1- Qt4 vs Qt5
About thread Qt4’s code is full compatibility with Qt5, however, one thing you could change: the connect of signals.https://wiki.qt.io/New_Signal_Slot_Syntax
2- SetPriority() method
Yep, it is normal, read:
https://doc.qt.io/qt-5/qthread.html#setPriority
https://doc.qt.io/qt-5/qthread.html#startYou can write thread→start(QThread::HighestPriority);
Pay attention at priority, set with carefully.
https://doc.qt.io/qt-5/qthread.html#Priority-enum
3- "CPU Usage"
https://doc.qt.io/qt-5/thread-basics.html -
Hi,
@fem_dev said in QThread + Shared Library:
When the main application calls the plugin::run() method, it will be started in a new thread....and when the run() method finish, this new thread is completly finished and quit?"
The run implementation you describe, unless I misunderstood, is a blocking call that will freeze your main thread the same way you would by not using a thread.
-
@fem_dev
Hi,
about QThread I suggest you to readhttps://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
The best way to use Qthread is using Worker.
Say this, I think could be useful QtConcurrent for your goal.
Regards
-
@CP71 Thank you...
I changed my code using this tutorial above and it work well! Thank you.
Here is my adapted source-code that runs inside my main application:
for (size_t step_id = 0; step_id < numSteps; step_id++) { QThread* thread = new QThread; pluginStack[step_id]->moveToThread(thread); connect(pluginStack[step_id], SIGNAL(error(QString)), this, SLOT(handleResults(QString))); connect(thread, SIGNAL(started()), pluginStack[step_id], SLOT(process())); connect(pluginStack[step_id], SIGNAL(finished()), thread, SLOT(quit())); connect(pluginStack[step_id], SIGNAL(finished()), pluginStack[step_id], SLOT(deleteLater())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); thread->setPriority(QThread::HighestPriority); // this line is not working! thread->start(); }
I have 3 questions:
1- Qt4 vs Qt5
I saw that this tutorial is from November 2011. And the usageQThread
reference version is Qt 4.7.
I know that theconnect()
notation has changed in Qt5.
I would like to know if I need to adapt another things in this code to move from Qt4 to Qt5.
Looking in the "Worker method" of the Qt 5QThread
documentation here, I think that may be some has need to change...but I don't have sure what or why.2- SetPriority() method
I added just a single line before thethread->start()
to set the thread priority. Look the code above.
But, when I added it, I got this run-time error below:QThread::setPriority: Cannot set priority, thread is not running
3- "CPU Usage"
Here is theprocess()
method that is called when the thread started:void Plugin::process() { qDebug("Start process"); size_t a = 0; for (size_t i = 0; i < 100; i++) { for (size_t j = 0; j < 1000000; j++) { a = i + j; } } qDebug() << "End process"; emit finished(); }
In my CPU Usage Monitor, I have 4 Cores.
When this plugin is running, I was expecting that one of theses cores should be usage at 100%, but it doesn't happening.
Why? -
You are welcome!
1- Qt4 vs Qt5
About thread Qt4’s code is full compatibility with Qt5, however, one thing you could change: the connect of signals.https://wiki.qt.io/New_Signal_Slot_Syntax
2- SetPriority() method
Yep, it is normal, read:
https://doc.qt.io/qt-5/qthread.html#setPriority
https://doc.qt.io/qt-5/qthread.html#startYou can write thread→start(QThread::HighestPriority);
Pay attention at priority, set with carefully.
https://doc.qt.io/qt-5/qthread.html#Priority-enum
3- "CPU Usage"
https://doc.qt.io/qt-5/thread-basics.html -
@CP71 Thank you so much! You are great!
-
@CP71 my last doubt:
Here is my new adapted source-code:
for (size_t step_id = 0; step_id < numSteps; step_id++) { // ===== RUNNING THE PLUGIN ===== // QThread* thread = new QThread; pluginStack[step_id]->moveToThread(thread); qRegisterMetaType<std::string>("std::string"); connect(pluginStack[step_id], &Plugin_API::sendMsg, this, &job_manager::handleResults); connect(thread, &QThread::started, pluginStack[step_id], &Plugin_API::process); connect(pluginStack[step_id], &Plugin_API::finished, thread, &QThread::quit); connect(pluginStack[step_id], &Plugin_API::finished, pluginStack[step_id], &Plugin_API::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(QThread::HighestPriority); // WAIT UNTIL THIS THREAD FINISH thread->wait(); // Not woking! }
When I added this
wait()
line, my application run the first thread (step_id = 0
), but after that, the whole system freezes.How can I execute all plugins in a serial sequence?
Thank you again!
-
@fem_dev
Hi,
you have set a infinite timeout in wait, check execution of first thread why it has never finished its work
https://doc.qt.io/qt-5/qthread.html#waitAnyway, purpose of threads is to allow processes to run concurrently, if you really need to wait the ending of thread’s working you don’t really need a thread.
But maybe I don't really understand your goal! ;)I don’t know your code or your plugin, but is so heavy to run all threads?
Let’s suppose I need to do the same thing (run a thread at a time), my approach should be:
- Run first thread and prossegue with main thread
- When the thread has finished its job I run the next thread and so on.
Clearly, every need has its story! ;)
So maybe the way described above isn’t good for you! -
@CP71 Thank you for your quick response. I will explain my case better here.
I want that my main app can be expanded by any another C++ developer. So each developed extension must be a shared library (plugin) that will be loaded in run-time by the main app.
Each plugin is a huge numerical calculation and it can take several hours to finish.
Finally, the user open the Main App GUI, set a list of plugins with parameters and click the button "start processing".
In my case, the output of the first plugin is the input of the next one.
So, I need that the plugin list execution MUST BE in a serial order.I can be wrong, but I was thinking to use the thread approach to get:
- No main application GUI freeze
- Enable to send and receive
signals
andslots
(from plugin to main app and vice versa)
Is there a better solution to this problem?
@CP71 said in QThread + Shared Library:
Let’s suppose I need to do the same thing (run a thread at a time), my approach should be:
- Run first thread and prossegue with main thread
- When the thread has finished its job I run the next thread and so on.
Yes, but how can I do it? I tried to use
wait()
but I have to set a timeout. I would like to wait until the thread finish. Just it. -
@fem_dev
Ok,
First you don’t need of wait.
I would do what I have already written, I run a thread at a time.
To do this I would write a slot in MainWindow or in your core where I manage my plugins loading.I’m not sure Qthread::HighestPriority is a good idea, my first choice would be default priority.
Your Init application function
{
….
step_id = 0; //Member of class
loadPlugin(step_id);
….
}void MyObject::loadPlugin( int step_id )
{
QThread* thread = new QThread;
pluginStack[step_id]->moveToThread(thread);qRegisterMetaType<std::string>("std::string"); connect(pluginStack[step_id], &Plugin_API::sendMsg, this, &job_manager::handleResults); connect(thread, &QThread::started, pluginStack[step_id], &Plugin_API::process); connect(pluginStack[step_id], &Plugin_API::finished, thread, &QThread::quit); connect(pluginStack[step_id], &Plugin_API::finished, myMainWindow or myCore, &MyObject::pluginLoaded); //New code connect(pluginStack[step_id], &Plugin_API::finished, pluginStack[step_id], &Plugin_API::deleteLater); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start();
}
//Here You manage serial plugin loading and possible checking
void MyObject::pluginLoaded()
{
object management and check ...step_id++; loadPlugin(step_id) ...
}
This is only an example, written at 5:30 AM when I read your post :D
There is QthreadPool too, but maybe in this case my first choice are QThreads
-
-
@fem_dev
The same to you!