写这几片博文,是自己对Qt线程使用的一些总结。会尽量写的详细,如果只是学习Qt线程的基本使用可以,参考转载的那篇《Qt线程基础》。
Qt线程的几种使用形式:
一些常用替换线程的方案:
1)如果需要对每个发来的信号都做出处理,那么有两种方式来解决,即在信号与槽的connect函数中明确第五个参数,将其设置 成DirectConnection方式阻塞时编程,或者设置成BlockingQueuedConnection按照加锁队列都可以很好的解决;
2)如果只需要对最新的信号做处理,那么这里也给出两种方案来处理:
a、槽所在线程设置bool状态,信号所在线程通过判定这个bool的状态来确定是否发送信号;
b、槽执行完毕,则向信号所在线程发送返回值,信号所在线程通过判定发来的这个返回值来判定是否继续对槽所在线程发送新的信号。
这里字面的意思,槽函数是类似中断形式执行。Qt5.7版本测试结果其实并不是这样的,在单线程直接连接的形式下,信号需要等到槽函数执行完毕,才触发新的信号,并且信号在槽函数执行期间应该是没有排队的。后续还需要测试,排队、多线程的信号与槽的工作模式。
说了这么多,都是为了了解Qt多线程周边知识,恰当的场景选择恰当的技术。
使用最多的应该是QThread,QThread的使用场景,双向交互,线程需要上报工作情况,外部控制线程内部工作情况,其使用方法:
首先我们看一下QThread的使用常识:
当然我们使用最多的是QThread于connect的关系,所以我们总结一下QThread于信号槽的使用规律:
首先我们要记住一些分析的铁律,紧随这些原则,将清洗的看清楚信号与槽在多线程中的工作,
如果需要槽函数在次线程执行:
class Thread:public QThread
{
Q_OBJECT
public:
Thread(QObject* parent=0):QThread(parent)
{
moveToThread(this);
}
public slots:
void slot_main()
{
qDebug()<<"from thread slot_main:" <
这就是上面提到的不好的用法,线程对象把自己移到次线程,这样他的槽函数就在次线程中行。 这可以工作,但这是 Bradley T. Hughes 强烈批判的用法。 这种情况connect是在主线程编写,主线程发出信号。
2.次线程run中发出信号,槽函数可以是发出信号对象自身的槽函数,自发自收,都是次线程中行。槽函数是QThread子类的槽函数,或者主线程中对象的槽函数,这里的种情况需要你指明run中connect中的连接方式,直连则该槽函数在次线程中执行(可能发生数据安全问题),列队则在主线程执行。
总结就是:分析发出信号的对象和接受信号对象所在的线程,再通过连接方式,判断槽函数在哪里执行。(小白在使用中就有在run中创建对象-因为多非槽函数都需要在次线程中执行,通过指针引出来,再connect与其他模块交互,指明连接方式为列队形式,所以相关执行都在次线程中执行)。这里记住moveToThread只能将槽函数移到次线程中,也就很容易理解QThread的推荐用法:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"main thread:"<
Qt跨线程的信号与槽,最终也是通过事件完成的,所以在使用QThread的时候,跨线程信号与槽run中必须开启时间循环。
QThreadPool是QObject的子类,QRunnable不是且是一个虚基类。QRunnable的子类对象+线程池(自定义或者全局线程池)固定搭配。
这里只说两点:
1、QRunnable 的autoDelete默认是true,及QRunnable 的子类run函数执行完,线程池会自动帮你删除该对象。
2、QRunnable 如何与外界进行通信
方法1:QRunnable并不继承自QObject类,因此无法使用信号/槽的方式与外界进行通信。我们就必须的使用其他方法,这里给大家介绍的是使用:QMetaObject::invokeMethod()函数、自定义事件也可以。
方法2:使用多重继承的方法,任务子类同时继承自QRunnable和QObject。
//书写形式--不使用信号与槽
void Runnable::run(){
QMetaObject::invokeMethod(m_receiver , "functionname" , Qt::QueuedConnection , Q_ARG(bool , enable));
//m_reveicer 为Runnable的成员变量
}
//继承QObject,使用信号与槽
void Runnable::run(){
connect(signalObj , SIGNAL(sig()) ,m_reveicer ,SLOT(slot()) , Qt::QueuedConnection )
//m_reveicer 为Runnable的成员变量 signalObj 运行在次线程的对象,还可以把这个对象的指针引到外部使用,连接它的槽函数,但连接方式一定要注意
}
该命名空间的函数都属为多线程服务的
这里对于几种不同的函数都有讲到:普通函数、常量成员函数、非常量成员函数。
该run函数介绍里面没有涉及事件循环,所以次线程发出信号可以(事件和inovkMeth也可以),但是跨线程槽,在次线程内应该是接受不到信号执行槽函数的。 (该部分主要是针对,单独函数在次线程执行,所以不涉及到信号触发次线程中执行槽函数,主要是函数执行进度,及外部对线程的控制)
运用QtConcurrent中的MapReduce模型进行简单的C++并行运算
Qt 之 Concurrent Map 和 Map-Reduce
Qt 之 Concurrent 框架
最后关于多线程相关的QFuture和QFutureWatcher:QFuture和QFutureWatcher
无论在分布式还是多线程中的异步调用都涉及到这个,Fututre这种机制Qt也是源于Java,在高性能分布式框架中,当产生异步调用时,跨机器调用时,这种高效等待调用结果方式,可以大大提高程序效率。