Qt线程:线程池——QThreadPool 和 QRunnable

QThreadPool

一、描述

QThreadPool 类管理 QThread 的集合。

QThreadPool 管理和回收单独的 QThread 对象,以减少使用线程的程序中的线程创建成本。

每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。

要使用 QThreadPool,需要子类化 QRunnable 并实现 run() 虚函数。然后创建该类的对象并将其传递给 QThreadPool::start()。QThreadPool 默认自动删除 QRunnable。

QThreadPool 是管理线程的低级类,Qt Concurrent 模块是更高级的方案。

  class HelloWorldTask : public QRunnable
  {
      void run() override
      {
          qDebug() << "Hello world from thread" << QThread::currentThread();
      }
  };

  HelloWorldTask *hello = new HelloWorldTask();
  // QThreadPool 取得 hello 的所有权并自动删除它
  QThreadPool::globalInstance()->start(hello);

二、属性成员

1、activeThreadCount : const int

线程池中的活动线程数。

2、expiryTimeout : int

在 expiryTimeout 毫秒内未使用的线程被认为已过期并将退出。此类线程将根据需要重新启动。 默认是30000 毫秒(30 秒)。

如果此属性为负,则新创建的线程不会过期,例如,直到线程池被销毁它们才会退出。

设置 expiryTimeout 对已经运行的线程没有影响。

建议在创建线程池之后调用 start() 之前设置此属性。

3、maxThreadCount : int

线程池使用的最大线程数。默认值是 QThread::idealThreadCount()。

注意:线程池将始终使用至少 1 个线程,即使 maxThreadCount 设置为零或负数。

4、stackSize : uint

线程池工作线程的堆栈大小。默认值为 0,表示 QThread 使用操作系统默认堆栈大小。

更改它对已创建或正在运行的线程没有影响。

5、threadPriority : QThread::Priority

新工作线程的线程优先级。

该属性的值仅在线程池启动新线程时使用。 更改它对已经运行的线程没有影响。

默认值为 QThread::InheritPriority,这使得 QThread 使用与 QThreadPool 对象所在的优先级相同的优先级。


三、成员函数

1、~QThreadPool()

销毁 QThreadPool。此函数将阻塞,直到所有QRunnable完成

2、void clear()

从队列中删除尚未启动的QRunnable对象。删除 QRunnable::autoDelete() 返回 true 的对象。

3、bool contains(const QThread *thread)

thread是否由线程池管理的线程。

4、[static] QThreadPool *globalInstance()

获取全局 QThreadPool 对象。

5、void start(QRunnable *runnable, int priority = 0)

保留一个线程并使用该线程来运行runnable,(除非该线程会使当前线程计数超过maxThreadCount())。runnable 会被添加到运行队列中。优先级参数可用于控制运行队列的执行顺序。

  • 如果 runnable->autoDelete() 返回 true,线程池将拥有 runnable 的所有权,并且 runnable->run() 返回后,线程池将自动删除 runnable。
  • 如果 runnable->autoDelete() 返回 false,则 runnable 的所有权仍归调用者所有。

在调用此函数后更改 runnable 的自动删除(QRunnable::setAutoDelete())会导致未定义的行为。

6、void start(std::function<void ()> functionToRun, int priority = 0)

重载函数。

例:

#include <QThreadPool>
#include <functional>

void showMSG()
{
    qDebug()<<"showMSG Thread ID:"<<QThread::currentThreadId();
    qDebug()<<"run showMSG";
}

int main(int argc, char *argv[])
{
    qDebug()<<"main Thread ID:"<<QThread::currentThreadId();
    std::function<void()> fun = showMSG;
    QThreadPool::globalInstance()->start(fun);
}

7、bool tryStart(QRunnable *runnable)

尝试保留一个线程来运行 runnable。

如果调用时没有可用线程,则此函数不执行任何操作并返回 false。 否则 runnable 将立即使用一个可用线程运行,并且返回 true。

关于runnable对象的所有权见上面的start()。

8、bool tryStart(std::function<void ()> functionToRun)

重载函数。

尝试保留一个线程来运行 functionToRun。

如果调用时没有可用线程,则此函数不执行任何操作并返回 false。 否则 functionToRun 立即使用一个可用线程运行,并且返回 true。

9、bool tryTake(QRunnable *runnable)

如果runnable尚未启动,则尝试从队列中删除指定的 runnable。

如果 runnable 尚未启动,则返回 true,并且 runnable 的所有权转移给调用者(即使 runnable->autoDelete() == true)。 否则返回false。

注意:如果runnable->autoDelete() == true,这个函数可能会删除错误的runnable。 这被称为 ABA 问题:原始的 runnable 可能已经执行并已被删除。 内存被重新用于另一个QRunnable 对象。因此,我们建议仅对未自动删除的QRunnable 对象调用此函数。

10、bool waitForDone(int msecs = -1)

让所有线程退出并从线程池中删除所有线程,最多等待 msecs 毫秒然后看退出和删除的结果。如果删除了所有线程返回 true,否则返回false。 如果 msecs 为 -1(默认值),则忽略超时(等待最后一个线程退出)。 

11、void reserveThread() 

保留一个线程,不考虑 activeThreadCount() 和 maxThreadCount()。

调用此函数后应调用releaseThread() 以允许重用它。

注意:此函数将始终增加活动线程的数量。 这意味着通过使用这个函数,activeThreadCount() 有可能返回一个大于 maxThreadCount() 的值。

12、void releaseThread()

释放先前通过调用reserveThread() 保留的线程。(处理activeThreadCount() 有可能返回一个大于 maxThreadCount() 的值的情况,以便线程池可以正确控制activeThreadCount())


QRunnable

一、描述

QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由重新实现的 run() 函数表示。
可以使用 QThreadPool 在单独的线程中执行代码。

二、成员函数

1、[static] QRunnable *create(std::function<void ()> functionToRun)

创建一个在 run() 中调用 functionToRun 的 QRunnable。默认情况下启用自动删除。

2、void run()

线程中要执行的代码。

3、void setAutoDelete(bool autoDelete)

设置是否启用自动删除。

如果开启了自动删除,QThreadPool会在调用run()后自动删除这个QRunnable 对象。

请注意,必须在调用 QThreadPool::start() 之前设置此标志。在 QThreadPool::start() 之后调用此函数会导致未定义的行为。

当启用 autoDelete 时,使用相同的 QRunnable 多次调用 QThreadPool::start() 会产生race condition(竞争条件:多个线程同时访问相同的共享数据,而造成数据的不一致性),因此不推荐使用。