1.2 多线程的创建和管理
Qt通过3种形式提供对线程的支持,分别是平台无关的线程类、线程安全的事件投递、跨线程的信号槽连接。
Qt中主要的线程类如下。
● QThread:提供跨平台的多线程解决方案。
● QThreadStorage:提供逐线程数据存储。
● QMutex:提供相互排斥的锁或互斥量。
● QMutexLocker:辅助类,可自动对 QMutex 加锁与解锁。
● QReadWriteLock:提供可以同时读写操作的锁。
● QReadLocker与QWriteLocker:自动对QReadWriteLock 加锁与解锁。
● QSemaphore:提供整型信号量,是互斥量的泛化。
● QWaitCondition:线程在被其他线程唤醒之前一直休眠。
QThread是Qt线程中的一个公共的抽象类,所有的线程类都是从QThread抽象类中派生的。需要实现QThread中的虚函数run,可通过start函数来调用run函数。在默认情况下,QThread::run会启动一个事件循环(QEventLoop::exec)。线程相关的函数如下。
● void run:线程体函数,用于定义线程的功能。
● void start:启动函数,用于将线程入口地址设置为run函数。
● void terminate:用于强制结束线程,不保证数据完整性和资源释放。
QCoreApplication::exec总是在主线程(执行main的线程)中被调用,不能从一个QThread中调用。在图形用户界面(GUI)程序中,主线程也称为GUI线程,是唯一允许执行GUI相关操作的线程。另外,必须在创建一个QThread前创建QApplication(或QCoreApplication)对象。
当线程启动和结束时,QThread会发送信号started和finished,可以使用isFinished和isRunning来查询线程的状态。
通过静态函数currentThreadId和currentThread返回当前正在执行的线程的标识,前者返回线程的ID,后者返回一个线程指针。
如果要设置线程的名称,可以在启动线程之前调用setObjectName。如果不调用setObjectName函数,那么线程的名称将是线程对象的运行时类型(QThread子类的类名)。
在新的线程中使用QThread类执行代码可通过以下几种方式。
1.继承QThread类
重写QThread的void run函数,在重写的函数中添加需要执行的代码。在以下代码中,NewThread类通过继承QThread类,重写run函数,实现一个新的线程。
#include <QThread>
class NewThread : public QThread
{
public:
explicit NewThread(QObject *parent = 0);
protected:
void run()
{
// 具体语句
}
2.使用 QThread::create
使用 QThread::create(要求Qt版本至少为Qt 5.10)直接创建一个QThread对象,它可接收一个函数类型的参数,当调用QThread::start时会在新的线程中执行此函数。以下代码为使用QThread::create函数的示例。
void function()
{
//具体语句
}
QThread *new_thread = QThread::create(function);
new_thread->start();
3.直接创建一个QThread对象
直接创建一个QThread对象,将一个QObject对象移动到此线程,则使用QMetaObject :: invokeMethod调用此对象的槽函数就会在新的线程中执行。以下代码为使用moveToThread的示例。
class Object : public QObject
{
Q_OBJECT
public:
explicit Object(QObject *parent = nullptr);
public slots:
void function() {
//具体语句
}
};
QThread *new_thread = new QThread();
Object *object = new Object();
object->moveToThread(new_thread);//将对象交给线程
new_thread->start();
QMetaObject::invokeMethod(object, "function");
QRunnable类可以和QThreadPool(线程池)配合使用。和QThread的第一种使用方法类似,开发者也需要通过继承并重写QRunnable::run函数,从而在新的线程中执行代码。以下代码为使用QRunnable的示例。
class Runnable : public QRunnable
{
public:
void run() override
{
//具体语句
}
};
QRunnable *runnable = new Runnable();
runnable->setAutoDelete(runnable);
QThreadPool::globalInstance()->start(runnable);
QtConcurrent类是基于QRunnable封装的上层接口,可以很方便地在一个新的线程中执行一个函数,也可以和QThreadPool配合使用,以满足更灵活的功能需求。以下代码为使用QtConcurrent的示例。
void function()
{
//具体语句
}
QtConcurrent::run(function);