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

QThread correct usage



  • Hi everyone!

    I have a images gallery application. All images I'm loading in QThread. In QtCreator all works fine (Release and Debug mode). But if I try to load my app just click on MyApplication.app I can't get images list only at first time. When I try to refresh the list I'm receiving error:

    2018-07-20 12:48:36.834 ShavSample[15543:506104] Persistent UI failed to open file file:///Users/andrewsh/Library/Saved%20Application%20State/com.consultica.ShavSample.savedState/window_1.data: Too many open files (24)
    QThreadPipe: Unable to create pipe: Too many open files
    QEventDispatcherUNIXPrivate(): Can not continue without a thread pipe
    Abort trap: 6
    

    I'm using Qt 5.11 on macOS 10.13.6.
    Create a worker:

    ShavGetImagesInfoWorker* worker = new ShavGetImagesInfoWorker(urls, callback);
    if(!ShavTaskManager::isObjectCreated()) {
        ShavTaskManager::initManager();
    }
    ShavTaskManager::shared()->startWork(worker);
    

    Start thread:

    if(worker) {
        m_tasks.append(worker);
        QThread* newThread = new QThread();
        connect(newThread, &QThread::started, worker, &ShavThreadWorker::run);
        connect(worker, &ShavThreadWorker::finished, this, &ShavTaskManager::onFinished);
        connect(worker, &ShavThreadWorker::finished, newThread, &QThread::quit);
        connect(newThread, &QThread::finished, newThread, &QObject::deleteLater);
        worker->moveToThread(newThread);
        newThread->start(static_cast<QThread::Priority>(priority));
    }
    

    All worker is a subclass from class:

    class ShavThreadWorker : public QObject
    {
            Q_OBJECT
        private:
            ShavTaskResult* m_result;
            ShavTaskResult* m_progress;
            QMutex m_mutex;
        protected:
            virtual QObject* doTaskAction();
            void updateProgress(QVariant value, QVariant total, QVariant userData = QVariant());
        public:
            explicit ShavThreadWorker(QJSValue callback, QObject *parent = nullptr);
            explicit ShavThreadWorker(QJSValue callback, QJSValue progress, QObject *parent = nullptr);
            ~ShavThreadWorker();
        signals:
            void finished();
        private slots:
            inline void onFinishedResult() {
                emit finished();
            }
        public slots:
            void run();
            inline void stop() {
                this->thread()->requestInterruption();
            }
     };
    

    and implementation of calls:

    QObject *ShavThreadWorker::doTaskAction() {
        return nullptr;
    }
    
    void ShavThreadWorker::updateProgress(QVariant value, QVariant total, QVariant userData) {
        if(userData.isNull()) {
            QMetaObject::invokeMethod(m_progress, "updateProgress", Qt::QueuedConnection,
                                      Q_ARG(QVariant, value), Q_ARG(QVariant, total));  //Main thread
        } else {
            QMetaObject::invokeMethod(m_progress, "updateProgress", Qt::QueuedConnection,
                                      Q_ARG(QVariant, value), Q_ARG(QVariant, total), Q_ARG(QVariant, userData));  //Main thread
        }
    }
    
    
    ShavThreadWorker::ShavThreadWorker(QJSValue callback, QObject *parent) : QObject(parent) {
        static int index = 0;
        setObjectName(QString("%1_%2").arg(metaObject()->className()).arg(index));
        index += 1;
    
        m_result = new ShavTaskResult(callback);
        connect(m_result, SIGNAL(finished()), SLOT(onFinishedResult()));
        m_progress = nullptr;
    }
    
    ShavThreadWorker::ShavThreadWorker(QJSValue callback, QJSValue progress, QObject *parent) : QObject(parent) {
        static int index = 0;
        setObjectName(QString("%1_%2").arg(metaObject()->className()).arg(index));
        index += 1;
    
        m_result = new ShavTaskResult(callback);
        connect(m_result, SIGNAL(finished()), SLOT(onFinishedResult()));
        m_progress = new ShavTaskResult(progress);
    }
    
    ShavThreadWorker::~ShavThreadWorker() {
    #ifdef QT_DEBUG
        qDebug()<<metaObject()->className()<<"was released!";
    #endif
    }
    
    void ShavThreadWorker::run() {
        m_mutex.lock();
        QObject* res = doTaskAction();
        if(m_result != nullptr) {
            res->moveToThread(qApp->thread());
            QMetaObject::invokeMethod(m_result, "sendResult", Qt::QueuedConnection, Q_ARG(QObject*, res));  //Main thread
        } else {
            onFinishedResult();
        }
        m_mutex.unlock();
    }


  • Having too many threads makes performance worse than having just 1.

    Rather than having a new thread having time you should distribute the work among QThread::IdealThreadCount threads



  • Thanks for the reply! Could you share links to the example how to use QThread::IdealThreadCount? In Qt docs I can't find examples for this.

    P.S. Also I try to use QThreadPool for call task But this also not working :(





  • Thanks, I will check!