Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 a foreach 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 the run() method finish, this new thread is completly finished and quit?"


    Another Youtube tutorial reference:



  • @fem_dev

    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#start

    You 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


  • Lifetime Qt Champion

    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 read

    https://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 usage QThread reference version is Qt 4.7.
    I know that the connect() 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 5 QThread 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 the thread->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 the process() 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?



  • @fem_dev

    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#start

    You 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#wait

    Anyway, 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 and slots (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



  • @CP71 Thank you so much for your attention and patience.

    I understood now!

    All the best!



  • @fem_dev
    The same to you!


Log in to reply