Qt之QThreadPool和QRunnable
简述
QRunnable 是所有 runnable 对象的基类,而 QThreadPool 类用于管理 QThreads 集合。
QRunnable 类是一个接口,用于表示一个任务或要执行的代码,需要重新实现 run() 函数。
QThreadPool 管理和循环使用单独的 QThread 对象,以帮助程序减少创建线程的成本。每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 访问。
详细描述
QThreadPool 支持多次执行相同的 QRunnable,通过调用 QThreadPool::tryStart(this) 从 run() 函数内。如果启用了 autoDelete,当最后一个线程退出 run() 函数,QRunnable 将被删除。多次调用 QThreadPool::start() 使用相同的 QRunnable,当启用 autoDelete 时会创建一个竞争条件,不推荐使用。
一定时间未使用线程将会到期,默认到期超时是 30000 毫秒(30秒)。可以使用 setExpiryTimeout() 来改变,设定一个负值,则会禁用到期机制。
调用 maxThreadCount() 查询使用线程的最大数量,如果需要,可以使用 setMaxThreadCount() 进行更改。默认情况下,maxThreadCount() 是 QThread::idealThreadCount()。activeThreadCount() 函数返回当前正在工作线程的数量。
注意: QThread::idealThreadCount() 提供了计算程序运行所在平台上支持的辅助线程的最佳数量 - 考虑到操作系统、处理器的数量和机器拥有的处理核的数量。对于只有一个处理器、一个处理核的机器,该函数或许会返回 1。对于拥有多个处理器和处理核的机器,返回值则会相应增大,这个数字不一定会与需要处理的文件个数完全匹配,因此需要将任务划分,这样的话,每个辅助线程(假设使用的辅助线程大于 1)都能得到一个和需要处理的文件数量相等的数值(当然,用文件数量目来划分任务或许不是在所有情况下都是最好的方法,例如:在一个数量为 20 的文件列表中,前 10 个文件很大,后 10 个 文件很小)。
reserveThread() 函数储备一个线程用于外部使用。当线程完成后,使用 releaseThread(),以便它可以被重新使用。从本质上讲,这些函数暂时增加或减少活跃线程的数量,并且当实现耗时的操作时对 QThreadPool 是不可见的,这比较有用。
注意: QThreadPool 是一个管理线程的低级类,高级替代品可以用 Qt Concurrent 模块。
基本使用
要使用 QThreadPool 的一个线程,子类化 QRunnable 并实现 run() 虚函数。然后创建一个对象,并把它传递给 QThreadPool::start() - 这会把可运行对象的拥有权赋给 Qt 的全局线程池,并可以让它开始运行。
class HelloWorldTask : public QRunnable
{
void run() {
qDebug() << "Hello world from thread " << QThread::currentThread();
}
} HelloWorldTask *hello = new HelloWorldTask(); // QThreadPool取得所有权,并自动删除 hello
QThreadPool::globalInstance()->start(hello);
默认情况下,当可运行对象结束时,线程池会自动将其删除,这也正是我们想要的效果。在某些情况下,如果必须由我们自己负责删除可运行的对象时,可以通过调用 QRunnable::setAutoDelete(false) 来阻止自动删除的发生。
自定义信号/槽
打开 QRunnable 所在头文件,会发现它并不继承自 QObject,也就是说,根本无法使用 QObject 的特性,例如:信号/槽、事件等。
为了便于使用,我们可以继承 QObject:
class HelloWorldTask : public QObject, public QRunnable
{
Q_OBJECT // 自定义信号
signals:
void finished(); public:
void run() {
qDebug() << "Hello Thread : " << QThread::currentThreadId();
emit finished();
}
};
使用时,连接信号槽即可:
class MainWindow : public QMainWindow
{
Q_OBJECT public:
explicit MainWindow(QWidget *parent = 0)
: QMainWindow(parent)
{
qDebug() << "Main Thread : " << QThread::currentThreadId(); // ...
HelloWorldTask *hello = new HelloWorldTask();
connect(hello, SIGNAL(finished()), this, SLOT(onFinished()));
QThreadPool::globalInstance()->start(hello);
// ...
} protected:
void closeEvent(QCloseEvent *event) {
if (QThreadPool::globalInstance()->activeThreadCount())
QThreadPool::globalInstance()->waitForDone(); event->accept();
} private slots:
void onFinished() {
qDebug() << "SLOT Thread : " << QThread::currentThreadId();
}
};
为了获得安全清楚,对于多线程应用程序来说,最好在终止程序之前,停止所有辅助线程。我们已经通过 closeEvent() 做到了这一点,可以确保在允许终止动作之前让任何活动的线程先结束掉。
顺便再介绍下所属线程,使用 qDebug 将各自的线程 ID 进行调试输出。结果如下:
Main Thread : 0xb308
Hello Thread : 0xb33c
SLOT Thread : 0xb308
显然,槽函数所在线程与主线程相同。
如果想要槽函数在次线程中执行,只需改变信号槽的连接方式:
connect(hello, SIGNAL(finished()), this, SLOT(onFinished()), Qt::DirectConnection);
这时,就得到了想要的结果啦: Main Thread : 0xb030
Hello Thread : 0xacf8
SLOT Thread : 0xacf8
事件的传递、数据的交互方式很多,这里只介绍了信号槽。当然还可以使用自定义事件,然后通过 调用 QApplication::sendEvent() 或 QApplication::postEvent() 发送;或者使用 QMetaObject::invokeMethod() 方式均可,这里就不再赘述了。
最新文章
- [LeetCode] Nth Digit 第N位
- GAE初探-一鼻子灰
- c-windows-1
- Android开发之事件分发和Listener
- Visual C++基础知识(win32exe)
- COCOA&#174; PROGRAMMING FOR MAC&#174; OS X (1)- Get Start
- javascript随机一个1-9的数字
- 轮播swiper配置选项
- TensorFlow环境搭建
- python基础学习第四天
- 小程序url传参如何写变量
- 关于ActionBar 左侧添加完返回后 点击无效的问题
- 数据库--mysql介绍
- C#基础笔记(第十八天)
- “全栈2019”Java多线程第四章:设置和获取线程名称
- 使用JSTL的xml:parse标签(或者说x:parse)出现异常NoSuchMethodError,找不到doc的setter method
- iframe是怎么跳转的
- VirtualBox 安装Centos7 无法上网 ping不同的解决方法
- WEB新手之sql注入
- swift的UIbutton
热门文章
- Elasticsearch 数据搜索篇
- Microsoft Bot Builder Overview
- (LeetCode 49)Anagrams
- HDU 5361 In Touch (2015 多校6 1009 最短路 + 区间更新)
- DBMS_METADATA中使用SESSION_TRANSFORM过滤不想获取的DDL
- 〖Linux〗使用root权限,telnet登录开发板
- jsp标签(jsp动作元素)
- 基于Java的在线聊天室
- ubuntu 14.04为/检查磁盘时发生严重错误的解决方法
- css background-position结合disaply:inline-block使用