简述

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() 方式均可,这里就不再赘述了。 

最新文章

  1. [LeetCode] Nth Digit 第N位
  2. GAE初探-一鼻子灰
  3. c-windows-1
  4. Android开发之事件分发和Listener
  5. Visual C++基础知识(win32exe)
  6. COCOA&#174; PROGRAMMING FOR MAC&#174; OS X (1)- Get Start
  7. javascript随机一个1-9的数字
  8. 轮播swiper配置选项
  9. TensorFlow环境搭建
  10. python基础学习第四天
  11. 小程序url传参如何写变量
  12. 关于ActionBar 左侧添加完返回后 点击无效的问题
  13. 数据库--mysql介绍
  14. C#基础笔记(第十八天)
  15. “全栈2019”Java多线程第四章:设置和获取线程名称
  16. 使用JSTL的xml:parse标签(或者说x:parse)出现异常NoSuchMethodError,找不到doc的setter method
  17. iframe是怎么跳转的
  18. VirtualBox 安装Centos7 无法上网 ping不同的解决方法
  19. WEB新手之sql注入
  20. swift的UIbutton

热门文章

  1. Elasticsearch 数据搜索篇
  2. Microsoft Bot Builder Overview
  3. (LeetCode 49)Anagrams
  4. HDU 5361 In Touch (2015 多校6 1009 最短路 + 区间更新)
  5. DBMS_METADATA中使用SESSION_TRANSFORM过滤不想获取的DDL
  6. 〖Linux〗使用root权限,telnet登录开发板
  7. jsp标签(jsp动作元素)
  8. 基于Java的在线聊天室
  9. ubuntu 14.04为/检查磁盘时发生严重错误的解决方法
  10. css background-position结合disaply:inline-block使用