A simple understanding of qthread

vishnumuthu Avatar

About the author


MenuBar

 

A simple understanding of qthread

The Example qt_qthread github link, Create a simple working for qt qthread with cmake to show the working of Multi-thread array.

* The example shows the reading the number of thread count

* The example shows creating multiple thread using dynamic array object and Qvector with signal/slot.

* The example shows difference between std::cout and qDebug() output.

 

MultiThread

C++ offers robust support for multithreading, allowing programs to perform multiple tasks concurrently, which can greatly enhance performance, especially on modern multi-core processors. Using the C++ Standard Library (since C++11), developers can create and manage threads. Multithreading enables the execution of independent tasks in parallel, such as handling I/O operations, performing computations, or managing real-time data.

C++ also provides synchronization mechanisms and condition variables to manage access to shared resources and ensure thread safety, avoiding issues like race conditions and deadlocks. Overall, C++’s multithreading capabilities make it ideal for high-performance and concurrent systems such as game engines, real-time processing, and large-scale applications.

In Qt, QThread is the class used to handle multithreading by allowing developers to move work to separate threads, enabling concurrency in an application. By subclassing QThread or using worker objects with moveToThread(), you can run tasks in parallel without blocking the main GUI thread, ensuring a responsive user interface. QThread provides signals like started() and finished() to track thread execution, and its event loop allows communication between threads through signals and slots, making it a powerful tool for managing background tasks, long-running operations, and real-time processing in Qt applications.

 

Create Thread as Array:

//thread_object.h
class Thread_object : public QThread
{
    Q_OBJECT
public:
    explicit Thread_object(QObject *parent = nullptr);

    void get_limit(int start, int end);

    void run();
private:
    int mStart, mEnd;
};

//thread_object.cpp
void Thread_object::get_limit(int start, int end)
{
    mStart = start;
    mEnd = end;
}
void Thread_object::run()
{
    for(int i = mStart; i <= mEnd; i++)
        qDebug()  << i ; //std::cout << i << std::endl;
}
//main.cpp
QThread cpuInfo; //get CPU info
    std::cout << cpuInfo.idealThreadCount() << std::endl;
    int index_start = 0, index_end = 99;
    int count_thread = cpuInfo.idealThreadCount();
    Thread_object *obj = new Thread_object[count_thread]; // Thread as Array
    int avg_count = (index_end - index_start) / count_thread;
    std::cout << "Avg: " << avg_count << std::endl;
    for(int i = 0; i < count_thread; i++)
    {
        if(i == 0) // Start
        {
            obj[i].get_limit(index_start, avg_count);
            obj[i].start();
        }
        else if(i == count_thread - 1) // End
        {
            obj[i].get_limit((avg_count * i) + 1, index_end);
            obj[i].start();
        }
        else // Middle
        {
            obj[i].get_limit((avg_count * i) + 1, avg_count * (i + 1));
            obj[i].start();
        }
    }

Explanation:

Here array is created dynamically Thread_object[count_thread] by reading the CPU thread count. The process is later split equally and assigned to the thread and the process is started by start() function.

 

Std::cout Vs qDebug()

std::cout and qDebug() are both used for outputting information in C++, but they serve different purposes. std::cout is a standard C++ stream used for printing messages to the console and is independent of any framework. It’s typically used in general-purpose C++ programming for logging or debugging but lacks advanced features like formatting or categorization without external libraries.

qDebug(), on the other hand, is part of the Qt framework and is designed for logging and debugging Qt applications. It provides richer functionality, such as automatic formatting of Qt types (e.g., QString, QList), output to different logging systems, and the ability to control verbosity levels. Additionally, qDebug() outputs can be disabled in release builds, making it more flexible for debugging in Qt environments.

From the image below, we can see the print is joined inspite of using std::endl.

qDebug() output
std::cout output
 

QT Signal – Slot:

Qt’s signal-slot mechanism is a powerful feature for communication between objects in Qt applications. It allows objects to send and receive messages, enabling loose coupling and event-driven programming. A signal is emitted when a particular event occurs, and a slot is a function designed to respond to that signal. Multiple slots can be connected to a single signal, and one signal can be connected to multiple slots, even across different threads. This system replaces traditional callbacks and enhances readability, scalability, and maintainability, making it easier to build interactive, event-driven applications in Qt.

 

Create Thread as QVector:

//thread_call.h
class thread_call : public QObject
{
    Q_OBJECT
public:
    explicit thread_call(QObject *parent = nullptr);

    QVector<Thread_object *> obj;

    void split_thread();

public slots:
    void read_processed(int data);

signals:

private:
    int thread_count;
};

// thread_call.cpp
thread_call::thread_call(QObject *parent)
    : QObject{parent}
{
    QThread cpuInfo; //get CPU info
    thread_count = cpuInfo.idealThreadCount();
    for(int i = 0; i < thread_count; i++)
    {
        Thread_object*create_process = new Thread_object; // Thread as QVector
        obj.append(create_process);
        connect(create_process, &Thread_object::send_finished,this, &thread_call::read_processed);
    }
}
void thread_call::split_thread()
{
    int index_start = 0, index_end = 99;
    int avg_count = (index_end - index_start) / thread_count;
    std::cout << "Avg: " << avg_count << std::endl;

    for(int i = 0; i < thread_count; i++)
    {
        if(i == 0) // Start
        {
            obj.at(i)->get_limit(index_start, avg_count);
            obj.at(i)->start();
        }
        else if(i == thread_count - 1) // End
        {
            obj.at(i)->get_limit((avg_count * i) + 1, index_end);
            obj.at(i)->start();
        }
        else // Middle
        {
            obj.at(i)->get_limit((avg_count * i) + 1, avg_count * (i + 1));
            obj.at(i)->start();
        }
    }
}
void thread_call::read_processed(int data)
{
    qDebug() << data;
}

Comment all and change the main.cpp to,

// main.cpp
    thread_call call;
    call.split_thread();

Explanation:

Here in Thread_call constructor, QVector is created and inserted with Thread_object object. A signal slot is connected between each Thread_object and the Thread_call method. The Thread_call process is later split equally and assigned to the thread object and the process is started by start() function.

  

Comments

2 responses to “A simple understanding of qthread”

  1. […]    Next post: A simple understanding of qthread […]

  2. […] on September 8, 2024Updated on September 8, 2024by vishnumuthuCategories:CPP Previous post: A simple understanding of qthread […]

Leave a Reply

Your email address will not be published. Required fields are marked *