[QT编程系列-29]:多线程机制 - 线程池

目录

前言:使用线程池的场景

第1章 线程池概述

1.1 什么是线程池

1.2 线程池的好处

第2章 QT QThreadPool

2.1 概述

2.2 QRunnable任务对象 =》 可执行的对象

2.3 QThreadPool代码示例

2.5 执行结果演示
​​​​​​​


前言:使用线程池的场景

(1)需要执行的任务或数据处理是临时性的、执行完就立即空闲,而不是无限制的循环任务

(2)需要执行的任务或数据处理是独立的

(3)需要执行的任务或数据处理的数量是不确定

第1章 线程池概述

1.1 什么是线程池

线程池是一个用于管理和复用多个线程的机制。它由一组预先创建的线程组成,可以在需要时分配任务给这些线程进行执行。

1.2 线程池的好处

使用线程池可以避免频繁地创建和销毁线程,从而减少线程创建和销毁的开销,并提高程序的性能和效率。

在线程池中,通常包含有限数量的线程,这些线程会在整个应用程序的生命周期内保持活动状态。当有任务需要执行时,线程池会选择一个空闲的线程来接收并执行任务。任务执行完毕后,线程会返回线程池,并等待分配下一个任务,而不是被销毁

线程池的好处包括:

  1. 减少线程创建和销毁的开销:因为线程的创建和销毁是比较耗时的操作,线程池可以复用已创建的线程,减少了这些开销,提高了效率。
  2. 管理线程的生命周期:线程池可以方便地管理线程的生命周期,无需手动创建和销毁线程,使线程的管理变得更加简单和自动化。
  3. 控制并发度:通过限制线程池的最大线程数,可以控制程序的并发度,避免资源过度占用和系统负荷过重。

第2章 QT QThreadPool

2.1 概述

Qt中提供了QThreadPool类来实现线程池。通过调用QThreadPoolstart()方法,可以将一个QRunnable任务对象添加到线程池中进行执行。QThreadPool会根据设置的最大线程数和任务队列,自动选择合适的线程来执行任务。

使用线程池可以很好地管理多线程任务,提高应用程序的性能和响应性,并减少线程管理的复杂性。同时,还可以通过合理设置线程池的属性,如最大线程数和任务队列大小,来控制线程池的运行方式和资源占用。

在Qt中,可以使用QThreadPool类来创建和管理线程池,实现多线程任务的分发和执行。线程池可以帮助管理多个线程,并根据需要分配任务,从而提高并发处理能力和资源利用率。

2.2 QRunnable任务对象 =》 可执行的对象

在Qt中,QRunnable是一个基类,用于定义可在线程中执行的任务对象

通过继承QRunnable类并实现其run()函数,可以创建自定义的可执行任务对象。

QRunnable可执行对象,可以被线程池多次调用、执行。

下面是一个简单的示例,展示了如何创建一个自定义的QRunnable任务对象:

// MyRunnable.h

#ifndef MYRUNNABLE_H
#define MYRUNNABLE_H

#include <QObject>
#include <QRunnable>

class MyRunnable : public QObject, public QRunnable {
    Q_OBJECT

public:
    explicit MyRunnable(QObject *parent = nullptr);
    void run() override;

signals:
    void resultReady(int result);
};


// MyRunnable.cpp

#include "MyRunnable.h"

MyRunnable::MyRunnable(QObject *parent)
    : QObject(parent)
{
}

//run函数不能是死循环,否则该可执行对象,一旦被执行,就无法结束。
//通常情况下,run函数是可以执行完成,可以被线程池反复执行。
void MyRunnable::run() {
    // 执行任务的逻辑
    int result = 0;
    // ...
    
    // 任务执行完成后,通过发射信号,将结果传递给主线程对象处理
    emit resultReady(result);
}

在上述示例中,MyRunnable类继承自QObjectQRunnable。在run()函数中,可实现具体的任务逻辑。这里只是简单地将result设为0,你可以根据实际需求编写任务逻辑。

使用QRunnable的好处是,它提供了一种将任务逻辑与线程解耦的方式。任务逻辑封装在run()函数中,不需要直接与线程进行交互。任务对象可以在多个线程之间共享和重用,由线程池管理任务的执行。

你可以在主线程或其他工作线程中创建并启动MyRunnable任务对象。当任务执行完毕后,可以通过发射信号来将执行结果传递给主线程,或者进行其他适当的处理。在主线程中,可以通过连接任务对象的信号来接收执行结果,并在槽函数中处理结果。

注意,当任务对象执行完毕后,请确保将其删除。若使用Qt的线程池(如QThreadPool)来管理任务对象的生命周期,则线程池会负责在任务执行完毕后销毁任务对象。否则,你需要在任务执行完毕后手动删除任务对象。

2.3 QThreadPool代码示例

下面是一个使用Qt线程池的示例:

// MyTask.h

#ifndef MYTASK_H
#define MYTASK_H

#include <QObject>
#include <QRunnable>

class MyTask : public QObject, public QRunnable {
    Q_OBJECT

public:
    explicit MyTask(QObject *parent = nullptr);
    void run() override;

signals:
    void resultReady(int result);
};


// MyTask.cpp

#include "MyTask.h"

MyTask::MyTask(QObject *parent)
    : QObject(parent)
{
}

void MyTask::run() {
    // 执行任务的逻辑
    int result = 0;
    // ...
    count++;
    qDebug() << "MyTask is running with "<< QThread::currentThread() ;
    //.......
    QThread::sleep(3);

    qDebug() << "MyTask is done for " << count;

    // 发射信号,将结果传递给主线程
    qDebug() << "MyTask send singal";
    emit resultReady(result);
}

// MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThreadPool>
#include "MyTask.h"

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

public slots:
    void handleTaskResult(int result);

private:
    QThreadPool *mThreadPool;
};

#endif // MAINWINDOW_H


// MainWindow.cpp

#include "MainWindow.h"
#include "MyTask.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), mThreadPool(new QThreadPool(this))
{
    //创建线程池,并由mThreadPool保存    

    // 设置线程池的最大线程数,默认为当前系统上可用的最大线程数
    mThreadPool->setMaxThreadCount(4);

    // 创建并添加任务到线程池中
    for (int i = 0; i < 10; ++i) 
    {
        //创建一个runable可执行的对象
        MyTask *task = new MyTask(this);

        //runable对象执行完成,通过resultReady通知主窗口线程,接收runable对象的执行结果
        //因此,连接信号槽
        connect(task, &MyTask::resultReady, this, &MainWindow::handleTaskResult);

        //把任务添加到线程槽中
        mThreadPool->start(task);
    }
}

MainWindow::~MainWindow() {
    mThreadPool->waitForDone(); // 等待所有任务执行完成
    delete mThreadPool;
}

void MainWindow::handleTaskResult(int result) {
    // 处理任务执行结果
    qDebug() << "Task result:" << result;
}

在上述示例中,MyTask类是一个自定义任务类,继承自QObjectQRunnable。在run方法中,实现了任务的具体逻辑,并通过发射resultReady信号将结果传递给主线程。

MainWindow是一个主线程的类,在构造函数中创建了一个QThreadPool对象,并设置了最大线程数。然后,创建了10个MyTask任务,并通过start方法将它们添加到线程池中进行执行。为了接收任务执行结果,使用connect函数将任务的resultReady信号连接到handleTaskResult槽函数。在槽函数中,可以处理任务的结果。

需要注意的是,在线程池中执行的任务必须派生自QRunnable类,并实现其run函数。任务可以通过在run函数中定义所需的逻辑,并在合适的时机发射信号来传递结果给主线程。

通过使用线程池,可以方便地管理和调度多个任务,充分利用系统资源,提高并发处理能力。可以根据实际需求设置线程池的最大线程数,使其适应当前系统的性能和负载情况。

2.5 执行结果演示

Main windows is running with QThread(0xab7770)

Runable task is created: 0

Runable task is added into thread pool: 0

Runable task is created: 1

Runable task is added into thread pool: 1

Runable task is created: 2

Runable task is added into thread pool: 2

MyTask is running with QThread(0x30ba4f0, name = "Thread (pooled)")

MyTask is running with QThread(0x30ba400, name = "Thread (pooled)")

Runable task is created: 3

MyTask is running with QThread(0x30b6060, name = "Thread (pooled)")

Runable task is added into thread pool: 3

Runable task is created: 4

MyTask is running with QThread(0x30cd1d0, name = "Thread (pooled)")

Runable task is added into thread pool: 4

Runable task is created: 5

Runable task is added into thread pool: 5

Runable task is created: 6

Runable task is added into thread pool: 6

Runable task is created: 7

Runable task is added into thread pool: 7

Runable task is created: 8

Runable task is added into thread pool: 8

Runable task is created: 9

Runable task is added into thread pool: 9

Main thread started: QThread(0xab7770)

Main thread finished QThread(0xab7770)

MyTask is done for 1

MyTask is done for 1

MyTask is done for 1

MyTask is done for 1

MyTask send singal

MyTask send singal

MyTask send singal

MyTask send singal

Receive Task result: 0

MyTask is running with QThread(0x30cd1d0, name = "Thread (pooled)")

MyTask is running with QThread(0x30b6060, name = "Thread (pooled)")

MyTask is running with QThread(0x30ba400, name = "Thread (pooled)")

MyTask is running with QThread(0x30ba4f0, name = "Thread (pooled)")

Receive Task result: 0

Receive Task result: 0

Receive Task result: 0

MyTask is done for 1

MyTask is done for 1

MyTask is done for 1

MyTask send singal

MyTask is done for 1

MyTask send singal

MyTask send singal

MyTask send singal

MyTask is running with QThread(0x30ba4f0, name = "Thread (pooled)")

Receive Task result: 0

Receive Task result: 0

Receive Task result: 0

MyTask is running with QThread(0x30b6060, name = "Thread (pooled)")

Receive Task result: 0

MyTask is done for 1

MyTask is done for 1

MyTask send singal

MyTask send singal

Receive Task result: 0

Receive Task result: 0

猜你喜欢

转载自blog.csdn.net/HiWangWenBing/article/details/131743602