文章目录
- QThread Class
- Detailed Description
- 成员函数说明
- QThread::QThread(QObject *parent = nullptr)
- void QThread::finished() [signal]
- void QThread::quit() [slot]
- void QThread::start(QThread::Priority priority = InheritPriority) [slot]
- void QThread::started() [signal]
- void QThread::terminate() [slot]
- QThread::~QThread() [virtual]
- QThread *QThread::create(Function &&f, Arg &&...args) [static]
- QThread *QThread::create(Function &&f) [static]
- QThread *QThread::currentThread() [static]
- Qt::HANDLE QThread::currentThread() [static]
- bool QThread::event(QEvent *event) [override virtual]
- QAbstractEventDispatcher *QThread::eventDispatcher() const
- int QThread::exec() [protected]
- void QThread::exit(int returnCode = 0)
- int QThread::idealThreadCount() [static]
- bool QThread::isFinshed() const
- bool QThread::isInterruptionRequested() const
- bool QThread::isRunning() const
- int QThread::loopLevel() const
- void QThread::msleep(unsigned long msecs) [static]
- QThread::Priority QThread::priority() const
- void QThread::requestinterruption()
- void QThread::run() [virutal protected]
- void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
- void QThread::setPriority(QThread::Priority priority)
- void QThread::setStackSize(uint stackSize)
- void QThread::setTerminationEnabled(bool enable = true) [static protected]
- void QThread::sleep(unsigned long secs) [static]
- uint QThread::stackSize() const
- void QThread::ussleep(unsigned long uses) [static]
- bool QThread::wait(unsigned long time = ULONG_MAX)
QThread Class
QThread提供了一个平台支持的类来管理线程
Detailed Description
QThread对象在程序中管理了一个线程,线程通过执行函数run()来开启,默认情况下,run()函数通过调用exec()函数来开启一个循环
有两种方式来开启一个线程
线程的创建方式
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter)
{
QString result;
/*这里放置一些处理的函数等*/
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
//C++中如果没有限定标志,那么默认的没有限定标志的成员变量将会认为是private型的
QThread workerThread;
public:
Controller()
{
Worker *worker = new Worker;
worker->moveToThread(&workerThread)
//当worker运行了父类QObject的槽函数deleteLater()后,将会触发workerThread的父类的槽函数finished()
connect(&workerThread, &Thread::finished, worker, &QObject::deleteLater);
//当worker发射doWork()信号的时候,本类(Controller)将会执行operate槽函数
connect(this, &Controller::operate, worker, &Worker::doWork);
//当本类发射handleResults()槽函数的时候,woker执行resultReady()槽函数
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&);
}
//QObject::deleteLater,是一个QObject的槽函数,
删除此QObject
当控件返回到事件循环的时候,该对象将会被删除,如果这个事件循环在此函数调用的时候还在运行(比如deleteLater()函数在QCoreApplication::exec()在执行前调用),这个对象将会在事件循环的开始的时候被删除,如果deleteLater()函数在主事件循环停止后被调用,这个对象将不会被删除,从Qt4.8开始,如果deleteLater()函数在一个对象中被调用,然后还存在一个没有事件循环的线程中,那这个对象将会在此线程结束的时候被析构掉
注意,从一个新的事件循环中进入和退出的过程中(比如一个模态的对话框)将不会执行延迟删除的动作,对于要删除的对象,控件必须返回到调用deleteLater()的事件循环,这不适用于在前一个嵌套事件循环仍在运行时删除的对象;一旦新的嵌套事件循环开始的时候,Qt的事件循环将会立刻删除这个对象
注意:多次的调用此函数是安全的,当提交第一个删除循环事件时,将从事件队列中删除对象的任何挂起的操作
在Worker类中的代码将会执行一个单独的线程,这样的话,我们可以很自由的通过信号和槽的方式来连接到Worker类中线程,在队列的机制的帮助下,我们在不同的线程中通过信号和槽的方式来进行通信是安全的。
另外一种方式是把代码放在一个单独的线程中去运行,我们可以创建QThread的子类并且重写run()函数,举个例子
class WorkThread : public QThread
{
Q_OBJECT
void run() override
{
QString result;
/*这里填写线程中需要做的事情*/
emit resultReady(result);
}
};
signal:
void resultReady(const QString &s);
//这个是主循环类,可以是一个窗体等,在这个主循环中开线程
void MyObject::startWorkerInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
//当线程中发射reaultReady()信号的时候,本类中执行操作线程得到的结果的函数
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResult);
//当线程完成并发射finished信号的时候,线程执行等待并析构自己的函数deleteLater
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
}
在这个例子中,线程将会在run函数完成并返回之后推出,在线程中将不会执行任何的事件循环机制,除非调用wxec()函数。
我们要记住QThread是线程关联在它的老的线程中的,而不是在一个新的线程中调用run()函数,这意味着所有的线程的队列槽函数和关联机制将会在老的线程中执行,因此,开发者如果想调用在新的线程中调用槽函数,必须使用工作-对象的方式,不能直接在子类中重写QThread父类的槽函数
/*
何为线程关联(转至QObject)
一个QObject的实例是一个线程关联的,或者说这个实例是存在于一个线程中的,当这个Qobject收到一个队列信号或者事件消息的时候,槽机制或者事件句柄将会运行在该QObject实例运行的线程中
注意:如果一个Qobject不在线程关联中(换句话说:这个线程返回值是zero),或者说这个QObject存在于一个没有事件循环的线程中,这样它就不能接受信号或者事件
默认情况下,这个QObject存在于它自己创建的线程中,一个object的线程关联可以被thread()来查询,还可以被moveToThread()来改变
所有的QObjec一定存在于和他父类相同的线程中,因此:
- 如果两个QObject存在连个不同的线程中,setParent()将会失效
- 当一个QObject移动到另一个线程中,那么它的子线程也将会移动到另一个线程中
- 如果该QObject有一个父类,那么movetoThread()函数将不会对这个QObject起作用
- 如果QObject是通过重写QThread::run()函数创建的,那么他们不能成为QThread的紫烈,因为QThread没有存在一个名为QThread::run()的线程中
注意:
一个QObject的成员变量不会自动成为它的子类,父子类的关系一定要通过一个指向子类的构造函数的指针,或者通过调用setParent()函数,如果没有这一步,对象的成员变量将会在调用moveToThread()的时候依旧保持在旧的线程中。
*/
回到QThread
不想队列槽函数或者消息机制,在QThread中直接调用方法将会将该方法直接在线程中进行,当派生了一个QThread的子类,需要记住该线程的构造函数是在老的线程中进行的,及时run()函数是在新的线程中执行,如果一个成员变量可以通过这两种方式来调用的话,那么就要检查一下是否该线程是安全的。
注意:在跨不同的线程的对象在通信的时候必须小心
线程的管理
QThread会通过一个信号告诉你什么时候开始和结束,使用start()和finish()信号,或者你可以使用IsFinished()和isRunning()函数来确定线程现在的状态
你也可以使用exit()和quit()函数类停止线程,在极端的情况下,你可以使用terminate()函数来强制的结束线程,但是这样做的话,是很危险而且是不好的,详细的可以看函数terminated()和setTerminationEnable()函数
从Qt4.8到现在,将存在线程中的QObject来解除联系是可行的,我们可以使用finished()信号来触发QObject::daleteLater()槽函数。
使用wait()函数来锁住这个线程,知道另外一个线程经结束指向,或者指定的时间结束。
QThread同样提供了稳定的,平台化的休眠支持:通过使用sleep(), msleep(), 和usleep()函数,这些函数从Qt5.0开始支持
注意:wait()和sleep()函数是这个线程中不必须的,因为Qt是事件驱动的架构,除了wait()函数,还会监听finished()信号,除了sleep()函数,还可以考虑QTimer类
静态方法currentThreadId()和currentThread()返回当前存在的线程的Id,返回的格式和平台指定的ID格式相关,后者返回一个QThread指针。
你可以调用setObjectName()在开始线程之前,这样你后面可以自己选指定的线程的Id,如果你没有调用这个函数,线程的名字将会是这个类的名字
成员函数说明
QThread::QThread(QObject *parent = nullptr)
构造一个新的QThread对象来管理一个新的线程,它的父类将是这个线程的拥有者,这个线程直到start()函数执行之前,将什么都不做
void QThread::finished() [signal]
这个信号将会在所管理的线程结束后发出
当这个信号发出的时候,事件循环将会停止运行,在此线程中不会有更多的事件被执行了,除了延期删除事件,这个信号可以连接到QObject::deleteLater()槽函数中,用来释放线程中的对象。
注意:如果使用terminate()函数来结束进程,线程将不会发任何信号出来。
注意:这个是一个私有的信号,只能通过信号连接而不能被用户来发射
void QThread::quit() [slot]
告诉线程的事件循环来退出并发返回0(如果成功),等同于调用QThread::exit(0)
如果这个线程没有任何事件循环,这个函数将什么都不做
void QThread::start(QThread::Priority priority = InheritPriority) [slot]
此函数通过调用run()函数来开始这个线程,操作系统将会根据预先设定的操作开始执行这个线程,如果线程正在运行中,那么这个函数将什么都不做。
优先级这个参数的影响取决于操作系统的设定,正常来讲,优先级这个参数如果操作系统不支持的话,则不会起作用(比如Liunx)
void QThread::started() [signal]
这个信号在执行start()函数后发射,在执行run()函数之前就发射
注意:这是一个私有的信号,只能被内部做连接到槽函数,而不能被用户发射
void QThread::terminate() [slot]
终止执行这个线程,这个线程可能又有可能不会立刻被终止,取决于操作系统的调度策略,使用terminate()函数之后使用QThread::wait()函数。
当一个线程被终止的时候,所有的等待这个线程的线程将会被唤醒。
警告:这个函数是危险的,线程可以在程序的任何地方被终止,线程可以在移动数据的时候终止,这样的话线程没有办法来恢复数据,会引发种种不幸,换句话说,最好不要用这个函数
这个函数可以显式的通过调用QThread::setTerminationEnabled()函数来显式的使能和禁用,在线程终止的时候调用这个函数可以防止线程被终止,知道设置这个函数位使能,看这个函数的文档来查看更多
QThread::~QThread() [virtual]
析构这个线程对象
注意:删除这个对象将不会停止这个对象所管理的线程,删除一个正在运行的线程(比如isFinished()函数返回false)将会导致程序崩溃,在删除这个对象前请等待finished()信号
QThread *QThread::create(Function &&f, Arg &&…args) [static]
创在一个新的QObject对象,然后将会使用参数args来执行这个函数f
这个新的线程没有开始–它一定要显示的调用start()函数,可以允许你来连接到信号中,移动QObject到这个线程中,选择这个新的线程的优先级等等,这个函数f将会在新的线程中被调用。
返回一个新构建的线程
注意:调用者拥有这个线程的操作的所有权
注意:这个函数仅仅在使用C++17的时候可以使用
警告:不要多次在一个线程中调用start()函数,这样将造成意想不到的后果
这个函数在Qt5.10中被介绍
QThread *QThread::create(Function &&f) [static]
创造一个将要执行函数f的线程对象
这个心的线程将不会开始-一定要显示的调用函数start(),允许你连接到信号中,移动这个QObject到一个线程中,选择线程的优先级等操作,这个函数将会在新的线程中被调用
返回一个新创建的QThread
注意:调用者拥有这个线程的操作的所有权
警告:不要多次在一个线程中调用start()函数,这样将造成意想不到的后果
这个函数在Qt5.10中被介绍
QThread *QThread::currentThread() [static]
返回一个指向QThread的对象指针,这个线程可以被这个指针所管理
Qt::HANDLE QThread::currentThread() [static]
返回一个当前运行的线程的句柄
警告:这个函数的仅仅用在内部的情况中,不可以被卸载任何用户的代码中
注意:在Windows中,这个函数返回一个DWORD,本质上是由Win32的GetCurrentThreadId()函数所返回的,
bool QThread::event(QEvent *event) [override virtual]
重载: QObject::event(QEvent *e)
QAbstractEventDispatcher *QThread::eventDispatcher() const
返回一个指向该线程的事件调度对象,如果线程中不存在事件指针,这个函数将返返回nullptr;
int QThread::exec() [protected]
进入一个事件循环直到函数exit()被调用,返回一个通过exit()函数的值,如果exit()函数调用了函数quit(),那么这个函数将会返回0。
这个函数意味着这个函数需要在run函数中才能调用,如果在线程中开启时间循环必须调用这个函数。
void QThread::exit(int returnCode = 0)
告诉线程的事件循环程序该退出了,并且返回一个代码0
在调用这个函数后,线程将结束由QEventLoop::exec()函数所开启的事件循环,并且QEventLoop::exec()函数将返回返回值
正常情况下,如果返回值是0则代表成功,如果是非0的话,则包含一个error
和C语言不同的是,这个函数返回给调用者一个值-这个事件进程结束了
在调用这个函数后,直到重新调用Qthread::exec()函数,线程中将不会有新的事件循环存在,如果时间循环没有开启,等下次调用QThread::exec()函数的时候,将会立刻返回
int QThread::idealThreadCount() [static]
返回操作系统中可以运行的理想的线程数量,他会对线程的核心做一个查询,包括真实的和逻辑的,如果系统的核心数不能被检测,那么这个函数将返回1
bool QThread::isFinshed() const
如果线程结束,这个函数返回true,如果没有结束,则返回false
bool QThread::isInterruptionRequested() const
返回是否线程中运行的任务可以结束,如果可以被结束,那么可以调用requestInterruption()函数来暂停线程。
此函数可以用在长时间运行的线程中,来实现暂停,不要检查或操作这个函数的返回值是否是安全的,但是在长期运行的函数中定期的检查或者操作,注意不要太频繁的调用这个函数,以保持较低的系统开销
void long_task(){
forever {
if (QThread::currentThread()->isInterruptionRequest()){
return;
}
}
}
bool QThread::isRunning() const
如果线程在运行,这个函数返回true,如果不在运行,这个函数返回false
int QThread::loopLevel() const
返回当前线程的循环等级
void QThread::msleep(unsigned long msecs) [static]
强制使线程休眠多少ms
如果需要改变一些给定参数的改变,请避免使用这个函数,可以使用信号槽的机制来岁输入条件进行改变,或者更使用事件循环句柄
注意:这个函数不能保证准确性,应用程序可能会休眠超过ms的级别,如果系统在高负荷的情况下。
QThread::Priority QThread::priority() const
返回正在运行的线程的优先级,如果线程不在运行,这函数返回继承的优先级
void QThread::requestinterruption()
请求线程被暂停,这个请求还取决于正在运行的代码,参考这些来判定是否可以暂停和以何种方式来暂停,这个函数不会停止任何正在运行事件循环的线程
void QThread::run() [virutal protected]
线程开始的标志点,在调用start()函数后,新开始的线程将调用此函数,默认的情况下是仅仅调用exec().
可以重写这个函数来实现更先进的线程管理,在这个函数中写return()将结束这个线程的运行
void QThread::setEventDispatcher(QAbstractEventDispatcher *eventDispatcher)
对事件调度操作设置线程的事件调度,使用这个函数的可能唯一的可能性是一旦在线程中没有安装事件调度者,这样的结果是,在线程使用start()函数开始线程前或者在QCoreApplication还没有在主线程生效之前,这个对象拥有这个方法的所有权
void QThread::setPriority(QThread::Priority priority)
这个函数设定当前运行的线程的优先级,如果这个线程没有在运行,那么这个函数将什么都不做,使用start()函数来启动一个带优先级的线程
线程的优先级可以用通过枚举常量QThread::Priority来设定,除了InheritPriority
优先级的效果取决于操作系统的内部机制,一般来讲,如果系统不支持对线程的优先级进行排序,则线程的优先级是没有意义的
void QThread::setStackSize(uint stackSize)
设定这个任务的最大的任务大小,任务大小最好要大于0,如果没有设置的话,那么这个任务的最大大小将由操作系统决定
警告:绝大多数系统都会分配任务的最小和最大大小,如果最小大小过小,那么这个任务可能不能被正常启动
void QThread::setTerminationEnabled(bool enable = true) [static protected]
使用这个布尔值来使能或者禁用是否可以使用teminate()函数终止线程,这个线程必须已经由Qthread来开始了
当这个值是false的时候,终止时禁用的,将来调用Qthread::teminate()函数是不起作用的,同时这个函数将会一直等设定允许后才起作用
当允许禁止的时候,将来调用QThread::teminate()函数将会正常的终止线程,这个函数没有返回值
void QThread::sleep(unsigned long secs) [static]
当前所运行的线程将会睡眠多少s
如果你需要等待给定的条件变更,请避免使用这个函数,同事,你可以使用信号槽机制来发起变更,或者使用一个事件句柄
注意:这个函数不会立刻执行,如果系统负载比较大,可能会延迟超过几秒
uint QThread::stackSize() const
返回线程的最大的任务内存,要不然则返回0
void QThread::ussleep(unsigned long uses) [static]
当前所运行的线程将会睡眠多少us
如果你需要等待给定的条件变更,请避免使用这个函数,同事,你可以使用信号槽机制来发起变更,或者使用一个事件句柄
注意:这个函数不会立刻执行,如果系统负载比较大,可能会延迟超过几秒
bool QThread::wait(unsigned long time = ULONG_MAX)
除非以下任何1个条件满足,封锁该线程
- 该QThread所关联的线程到完成的执行(当这个线程从run()函数中return出来)这个函数将会返回trun,同事这个函数在该线程还没有启动的时候也会返回true
- ms级别的时间消逝了,如果时间是最大的,那么这个等待将永远不会潮湿,这个函数将会在等待潮湿的时候返回false
这个函数类似POSIX pthread_join()函数