线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
计算机程序常编写专门的workhorse线程执行密集计算,从而提高了程序的执行效率。
今天主要介绍Qt线程四种不同的使用方式,下面结合案例具体说明一下。
一、继承 QThread, 重写 run() 方法,在run()方法中进行费时操作。
这个方法在很多教程上都可以了解到,大家第一个接触的Qt线程操作应该就是它。它的优点是便于理解,缺点是操作繁琐而且需要自己处理线程安全。下面是一个简单的示例。
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
QString result;
/* ... here is the expensive or blocking operation ... */
/**
while(true)
{
qInfo() << "xxxxx";
}
*/
emit resultReady(result);
}
signals:
void resultReady(const QString&);
};
// ----
void CcObject::startWorkThread()
{
auto *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &CcObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
另外需要注意如下几点:
- 因为workerThread对象创建于旧线程, 所以它的slots函数和调用的方法都将在旧线程中执行。
- 非线程安全。
- 依赖于Qt的事件循环。
二、使用 QObject的 moveToThread方法。
官方比较推荐的方法。
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
使用时注意:
- 此方法同样依赖于Qt的事件循环。
- 同样需要注意线程同步问题。
三、使用 QThreadPool::globalInstance() 线程池操作。
QThreadPool用于管理和回收单个QThread对象,以帮助减少使用线程的程序中的线程创建成本。每个Qt应用程序都有一个全局QThreadPool对象,可以通过调用globalInstance()进行访问。
以下是一个简单的使用案例:
class Task : public QRunnable
{
void run() override
{
// 费时操作
qDebug() << "thread run at:" << QThread::currentThread();
}
};
auto *task = new Task();
QThreadPool::globalInstance()->start(task);
注意事项:
- 默认情况下, 用户在调用 QThreadPool::globalInstance()->start(task); 之后不需要再关心task对象的释放问题,因为 QThreadPool 会自动删除 task。当然也可以通过 QRunnable :: setAutoDelete(bool) 调整策略。
- 在一定时间内未使用的线程将过期。默认的到期超时为30000毫秒(30秒)。可以使用setExpiryTimeout()进行调整。当设置到期超时为负值时将禁用到期机制。
- maxThreadCount()方法可以查询最大线程数,也可以使用setMaxThreadCount()进行调整。默认的maxThreadCount()是QThread::idealThreadCount()。
- QThreadPool是一个用于管理线程的低级类,高级玩法请参照 QtConcurrent模块。
四、QtConcurrent模块。(高级玩法)
QtConcurrent模块扩展了Qt Core模块中提供的基本线程支持,并简化了可在所有可用CPU内核上并行执行的代码的开发。
The QtConcurrent namespace provides high-level APIs that make it possible to write multi-threaded programs without using low-level threading primitives such as mutexes, read-write locks, wait conditions, or semaphores. Programs written with QtConcurrent automatically adjust the number of threads used according to the number of processor cores available. This means that applications written today will continue to scale when deployed on multi-core systems in the future.
此模块简化了用户操作,强烈推荐使用。
下面是一个简单的示例。
// 方法参数为 Function
// 可以处理返回值
QFuture<int> res = QtConcurrent::run([](){
// 费时操作
return 0;
});
// 可以等待处理完成
// 1.阻塞执行 不阻塞事件循环
// while(!res.isFinished()) {
// wait
// QApplication::processEvents(QEventLoop::AllEvents, 200);
// }
// 2. 阻塞等待
res.waitForFinished();
// 处理返回值
int result = res.result();
注意事项:
- 需要引入 Qt Concurrent模块。
- 可以使用 QFutureWatcher 监视 QFuture。以下是一个官方示例:
// Instantiate the objects and connect to the finished signal.
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, QFutureWatcher<int>::finished, &myObject, &MyClass::handleFinished);
// Start the computation.
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
3. 非线程安全。