The use of thread pool in Qt

1. The principle of thread pool

When we use threads, we create a thread, which is very simple to implement, but there will be a problem: if there are a large number of concurrent threads, and each thread ends after executing a short task, so frequent Creating threads will greatly reduce the efficiency of the system, because frequent thread creation and thread destruction take time.

So is there a way to make threads reusable, that is, after executing a task, it is not destroyed, but can continue to execute other tasks?

A thread pool is a form of multithreading in which tasks are added to a queue and then automatically started after threads are created. Thread pool threads are all background threads. Each thread uses the default stack size, runs at the default priority, and is in a multithreaded apartment. If a thread is idle in managed code (such as waiting for an event), the thread pool will insert another worker thread to keep all processors busy. If all thread pool threads are kept busy all the time, but the queue contains pending work, the thread pool will create another worker thread after a while but the number of threads will never exceed the maximum. Threads that exceed the maximum can be queued, but they won't start until other threads have finished.

There is a concept of thread pool in each programming language, and many languages ​​directly provide thread pool, which can be used directly as a programmer. Let me introduce the implementation principle of thread pool: the main components of thread
pool It is divided into three parts, and these three parts can work together to get a complete thread pool:

  1. The task queue stores the tasks that need to be processed, and these tasks are processed by the working threads.
  • Through the API functions provided by the thread pool, add a pending task to the task queue, or delete it from the task queue.
  • Processed tasks are removed from the task queue
  • The user of the thread pool, that is, the thread that calls the thread pool function to add tasks to the task queue is the producer thread
  1. Working threads (consumers of task queue tasks), N
  • A certain number of worker threads are maintained in the thread pool. Their function is to read the task queue continuously, take out tasks from it and process them.
  • The working thread is equivalent to the consumer role of the task queue.
  • If the task queue is empty, the working thread will be blocked (using condition variable/semaphore blocking)
  • If there is a new task after blocking, the producer will unblock and the worker thread will start working
  1. Manager thread (does not process tasks in the task queue), 1
  • Its task is to periodically detect the number of tasks in the task queue and the number of worker threads in a busy state
  • When there are too many tasks, some new worker threads can be created appropriately
  • When there are too few tasks, some working threads can be properly destroyed
    insert image description here

2. QRunnable

To use the thread pool in Qt, you need to create a task first, and each task added to the thread pool needs to be a QRunnable type, so you need to create a subclass in the program to inherit the QRunnable class, and then rewrite the run() method, in this Write the task to be executed in the thread pool in the function, and pass this subclass object to the thread pool, so that the task can be processed by a working thread in the thread pool.
There are not many commonly used functions of the QRunnable class, mainly to set whether the task object needs to be automatically destructed after it is passed to the thread pool.

// 在子类中必须要重写的函数, 里边是任务的处理流程
[pure virtual] void QRunnable::run();

// 参数设置为 true: 这个任务对象在线程池中的线程中处理完毕, 这个任务对象就会自动销毁
// 参数设置为 false: 这个任务对象在线程池中的线程中处理完毕, 对象需要程序猿手动销毁
void QRunnable::setAutoDelete(bool autoDelete);
// 获取当前任务对象的析构方式,返回true->自动析构, 返回false->手动析构
bool QRunnable::autoDelete() const;

Create a task class to be added to the thread pool as follows:

class MyWork : public QObject, public QRunnable
{
    
    
    Q_OBJECT
public:
    explicit MyWork(QObject *parent = nullptr)
    {
    
    
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{
    
    }
}

In the above example, the MyWork class is a multiple inheritance. If you need to use Qt’s signal slot mechanism for data transfer in this task, you must inherit the QObject class. If you don’t use signal slots to transfer data, you can not inherit it, just inherit QRunnable will do.

class MyWork :public QRunnable
{
    
    
    Q_OBJECT
public:
    explicit MyWork()
    {
    
    
        // 任务执行完毕,该对象自动销毁
        setAutoDelete(true);
    }
    ~MyWork();

    void run() override{
    
    }
}

3. QThreadPool

The QThreadPool class in Qt manages a group of QThreads, and maintains a task queue inside. QThreadPool manages and recycles individual QThread objects to help reduce thread creation costs in programs that use threads. Every Qt application has a global QThreadPool object which can be accessed by calling globalInstance(). You can also create a QThreadPool object separately.
Commonly used API functions of the thread pool are as follows:

// 获取和设置线程中的最大线程个数
int maxThreadCount() const;
void setMaxThreadCount(int maxThreadCount);

// 给线程池添加任务, 任务是一个 QRunnable 类型的对象
// 如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理
void QThreadPool::start(QRunnable * runnable, int priority = 0);
// 如果线程池中没有空闲的线程了, 直接返回值, 任务添加失败, 任务不会添加到任务队列中
bool QThreadPool::tryStart(QRunnable * runnable);

// 线程池中被激活的线程的个数(正在工作的线程个数)
int QThreadPool::activeThreadCount() const;

// 尝试性的将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了
bool QThreadPool::tryTake(QRunnable *runnable);
// 将线程池中的任务队列里边没有开始处理的所有任务删除, 如果已经开始处理了就无法通过该函数删除了
void QThreadPool::clear();

// 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象
static QThreadPool * QThreadPool::globalInstance();

Generally, we don't need to create thread pool object in Qt program, just use the thread pool global object provided by Qt for each application directly. After getting the thread pool object, call the start() method to add a task to the thread pool, and this task can be processed by the thread pool inside the thread pool. Using the thread pool is much more than creating threads by yourself. The threading approach is simpler and easier to maintain.

The specific usage is as follows:

mywork.h

class MyWork :public QRunnable
{
    
    
    Q_OBJECT
public:
    explicit MyWork();
    ~MyWork();

    void run() override;
}

mywork.cpp

MyWork::MyWork() : QRunnable()
{
    
    
    // 任务执行完毕,该对象自动销毁
    setAutoDelete(true);
}
void MyWork::run()
{
    
    
    // 业务处理代码
    ......
}

mainwindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    
    
    ui->setupUi(this);

    // 线程池初始化,设置最大线程池数
    QThreadPool::globalInstance()->setMaxThreadCount(4);
    // 添加任务
    MyWork* task = new MyWork;
    QThreadPool::globalInstance()->start(task);    
}

Guess you like

Origin blog.csdn.net/houxian1103/article/details/131438335