Efficient Concurrency in Qt Using QRunnable and QThreadPool

Thread pools offer an efficient approach to managing concurrent tasks by reusing a fixed number of threads rather then repeatedly creating and destroying them. This minimizes overhead and improves performance, especially when handling numerous short-lived operations. In Qt, the QThreadPool and QRunnable classes provide a high-level interface for implementing such thread pool–based concurrency.

Understanding QRunnable

QRunnable is an abstract base class representing a unit of work that can be executed by a thread pool. Unlike QThread, it does not manage thread lifecycle directly. Instead, it encapsulates logic within its pure virtual run() method. Key features include:

  • run(): Must be overridden to define task behavior.
  • setAutoDelete(bool): When enabled (default is false), the runnable deletes itself after execution, preventing memory leaks.
  • setPriority(QThread::Priority): Allows setting execution priority relative to other runnables.

Managing Threads with QThreadPool

QThreadPool manages a collection of reusable threads and schedules QRunnable instances across them. It handles thread creation, reuse, and cleanup automatically. Common methods include:

  • globalInstance(): Returns a shared, application-wide thread pool instance.
  • start(QRunnable*): Queues a task for execution.
  • setMaxThreadCount(int): Limits the number of concurrently running threads to avoid resource exhaustion.
  • waitForDone(): Blocks until all queued tasks complete—useful for synchronization before shutdown.

Practical Implementation

The following example demonstrates submitting multiple retry-capable tasks to a thread pool.

worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QRunnable>
#include <QString>
#include <QDebug>
#include <QThread>

class Task : public QRunnable {
public:
    explicit Task(const QString& name, int maxRetries = 3);
    void run() override;

private:
    bool simulateOperation();
    QString m_name;
    int m_maxRetries;
};

#endif // WORKER_H

worker.cpp

#include "worker.h"
#include <QRandomGenerator>

Task::Task(const QString& name, int maxRetries)
    : m_name(name), m_maxRetries(maxRetries) {
    setAutoDelete(true);
}

void Task::run() {
    int attempt = 0;
    bool succeeded = false;

    while (attempt < m_maxRetries && !succeeded) {
        ++attempt;
        qDebug() << "Executing:" << m_name << "| Attempt:" << attempt;
        succeeded = simulateOperation();

        if (!succeeded) {
            qDebug() << "Failed, retrying:" << m_name;
            QThread::sleep(2);
        }
    }

    if (succeeded) {
        qDebug() << "Completed:" << m_name;
    } else {
        qDebug() << "Aborted after retries:" << m_name;
    }
}

bool Task::simulateOperation() {
    return QRandomGenerator::global()->bounded(2) == 1; // 50% success rate
}

main.cpp

#include <QCoreApplication>
#include <QThreadPool>
#include "worker.h"

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);

    auto* pool = QThreadPool::globalInstance();
    pool->setMaxThreadCount(4);

    pool->start(new Task("Download A"));
    pool->start(new Task("Process B"));
    pool->start(new Task("Validate C"));
    pool->start(new Task("Upload D", 2)); // Only 2 retries
    pool->start(new Task("Sync E"));

    pool->waitForDone();
    return 0;
}

Sample output may resemble:


Executing: "Download A" | Attempt: 1
Completed: "Download A"
Executing: "Process B" | Attempt: 1
Completed: "Process B"
Executing: "Validate C" | Attempt: 1
Completed: "Validate C"
Executing: "Upload D" | Attempt: 1
Failed, retrying: "Upload D"
Executing: "Upload D" | Attempt: 2
Completed: "Upload D"
Executing: "Sync E" | Attempt: 1
Completed: "Sync E"

This pattern enables scalable, maintainable concurrency: tasks are decoupled from thread management, automatic cleanup prevents leaks, and the thread pool ensures optimal resource utilization without manual intervention.

Tags: Qt QRunnable QThreadPool C++

Posted on Mon, 10 Aug 2026 16:30:56 +0000 by vbzoom.com