Qt05多线程和线程池
发表于更新于
字数总计:2.2k阅读时长:7分钟阅读量: 济宁
Qt多线程Qt05多线程和线程池
wyp 一, 最简单的多线程QtConcurrent::run()
1 2
| QFuture<T> QtConcurrent::run(Function function, ...) QFuture<T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
|
参数function需要外部函数:(或者lambda函数),后面也可跟外部函数的参数。
1
| extern void func(QString str);
|
QtConcurrent :: run()也接受指向成员函数的指针。第一个参数必须是一个const引用或一个指向该类实例的指针。const成员函数一般传递 常量引用 (const reference),而非常量成员函数一般传递 指针 (pointer)
- 在VS环境中需要引用: #include”QtConcurrent/qtconcurrentrun.h”
简单的说,QtConcurrent::run()函数会在一个单独的线程中执行,并且该线程取自全局QThreadPool,该函数的返回值通过QFuture API提供。
请注意:该函数可能不会立即运行; 函数只有在线程可用时才会运行。通过QtConcurrent::run()返回的QFuture不支持取消、暂停,返回的QFuture只能用于查询函数的运行/完成状态和返回值。
在主程序threadtest.h中声明成员函数(并添加引用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <QtWidgets/QMainWindow> #include "ui_threadtest.h" #include"workThread.h" #include"qthread.h" #include"QtConcurrent/qtconcurrentrun.h" #pragma execution_character_set("utf-8") class Threadtest : public QMainWindow { Q_OBJECT public: Threadtest(QWidget *parent = Q_NULLPTR); private: Ui::ThreadtestClass ui; void work(); };
|
在主程序threadtest.cpp中调用成员函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "threadtest.h" #include"qdebug.h"
Threadtest::Threadtest(QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); connect(ui.btn_start, &QPushButton::clicked, [=]() { QtConcurrent::run(this, &Threadtest::work); }); } void Threadtest::work() { qDebug() << "子线程运行:" << QThread::currentThreadId(); QThread::sleep(5); qDebug() << "子线程结束:" << QThread::currentThreadId(); }
|
点击按钮即可触发子线程运行。

在主程序threadtest.h中声明外部函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <QtWidgets/QMainWindow> #include "ui_threadtest.h" #include"workThread.h" #include"qthread.h" #include"QtConcurrent/qtconcurrentrun.h" #pragma execution_character_set("utf-8") class Threadtest : public QMainWindow { Q_OBJECT
public: Threadtest(QWidget *parent = Q_NULLPTR); private: Ui::ThreadtestClass ui; }; extern void func(QString str);
|
在主程序threadtest.cpp中调用外部函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "threadtest.h" #include"qdebug.h"
Threadtest::Threadtest(QWidget* parent) : QMainWindow(parent) { ui.setupUi(this); connect(ui.btn_start, &QPushButton::clicked, [=]() { QtConcurrent::run(func,QString("extern")); }); } void func(QString str) { qDebug() << "子线程运行:" << QThread::currentThreadId()<< str; QThread::sleep(5); qDebug() << "子线程结束:" << QThread::currentThreadId()<< str; }
|

二,线程池
2.1 线程池原理
我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件), 则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值。超过最大值的线程可以排队,但他们要等到其他线程完成后才启动。

2.2 QRunnable
在 Qt 中使用线程池需要先创建任务,添加到线程池中的每一个任务都需要是一个 QRunnable 类型,因此在程序中需要创建子类继承 QRunnable 这个类,然后重写 run() 方法,在这个函数中编写要在线程池中执行的任务,并将这个子类对象传递给线程池,这样任务就可以被线程池中的某个工作的线程处理掉了。
1 2 3 4 5 6 7 8
| [pure virtual] void QRunnable::run();
void QRunnable::setAutoDelete(bool autoDelete);
bool QRunnable::autoDelete() const;
|
例如:创建一个要添加到线程池中的任务类,处理方式如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MyWork : public QObject, public QRunnable { Q_OBJECT public: explicit MyWork(QObject *parent = nullptr) { setAutoDelete(true); } ~MyWork();
void run() override{} }
|
在上面的示例中 MyWork 类是一个多重继承,如果需要在这个任务中使用 Qt 的信号槽机制进行数据的传递就必须继承 QObject 这个类,如果不使用信号槽传递数据就可以不继承了,只继承 QRunnable 即可。
2.3 QThreadPool
Qt 中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。也可以单独创建一个 QThreadPool 对象使用。
线程池常用的 API 函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int maxThreadCount() const; void setMaxThreadCount(int maxThreadCount);
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();
static QThreadPool * QThreadPool::globalInstance();
|
一般情况下,我们不需要在 Qt 程序中创建线程池对象,直接使用 Qt 为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用 start() 方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。
具体的使用方式如下:
1 2 3 4 5 6 7 8 9
| class MyWork :public QRunnable { Q_OBJECT public: explicit MyWork(); ~MyWork();
void run() override; }
|
1 2 3 4 5 6 7 8 9 10
| MyWork::MyWork() : QRunnable() { setAutoDelete(true); } void MyWork::run() { ...... }
|
1 2 3 4 5 6 7 8 9 10 11 12
| 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); }
|
由此看见,线程池的操作和QThread的run重写类似。使用线程池可以最大限度的利用线程,减少资源的浪费
比如我们需要同时处理三个事件:生成随机数,冒泡排序,快速排序(先生成随机数,然后再对其同时进行冒泡排序和快速排序)
用QThread的run重写的话,需要开启三个线程。
但是用线程池的话,它可以根据事件的长短,(将生成随机数和冒泡排序放在同一个线程去处理)只需要开启两个线程即可。
转载自:唯有自己强大 如有侵权,在下方评论 立刻删除。