QT5 QThread
QT5 QThread
https://blog.csdn.net/zy19940906/article/details/54412600
序
QThread的线程用法上与std::thread相比有较大的区别,4.4版本之前是继承的方式来使用线程(个人猜测可能是因为那会儿c++11还没出来,std::function和std::bind没有,所以继承是实现消息回调比较方便的方式,当然仅仅是猜测,有兴趣可以查证),但4.4之后开始,官方建议不要再用继承的方式来使用线程,而是通过信号槽的方式来取代。
测试环境:Qt5.7,vs2015。
一、QThread:
如下图所示:
看不清楚图可以直接在官网看:传送门
1、继承自QObject(截图没截上)
2、启动线程:start()函数,启动后调用run()函数,run()执行完之后退出线程。
3、wait:有点类似std::thread 的join,但是需要指定时间,并且不是线程run函数结束后自动返回,如果不指定,默认会一直等待。所以我一般在用的过程中,需要退出的时候(或者调用quit),再wait。
二、两种用法:
既然QThread有两种用法,那么就简单介绍下吧:
图里已经说得很清楚了,那我就不在多说:
头文件:
class QtThreadFuncByThread : public QThread { public: void SetLoop(int loop); int GetSum(); protected: virtual void run(); private: int _Loop = 0; int _Sum = 0; };
void QtThreadFuncByThread::SetLoop(int loop) { this->_Loop = loop; } int QtThreadFuncByThread::GetSum() { return this->_Sum; } void QtThreadFuncByThread::run() { for(int i = 0; i < this->_Loop; ++i) { this->_Sum++; } int id =(int)QThread::currentThreadId(); emit Log::GetInstance()->LogStr(QString("qt继承方式子线程id: %1;"). arg(id)); }
使用:
QtThreadFuncByThread thread; thread.SetLoop(loop); thread.start(); thread.wait(100);
2、信号槽方式(推荐用法):
至于为啥推荐呢,直接给个传送门吧:传送门
至于代码,官网给了例子:传送门
我再写个简化版的:
QThread * thread = new QThread; QtThreadFuncClass* funcclass = new QtThreadFuncClass; funcclass->SetLoop(loop); funcclass->moveToThread(thread); QObject::connect(thread, &QThread::started, funcclass, &QtThreadFuncClass::ThreadFunc, Qt::DirectConnection); thread->start(); thread->wait(100);
简而言之,你需要做的是把QObject对象,movetothread去,否则你调用的信号槽仍然是在主线程。
二、互斥量,锁,条件变量,原子操作及其他:
其实std::thread有的那些互斥量,自解锁,条件变量,future,原子操作等,Qt里面也能找到对应的类,只是用法,和一些细节性的功能不太一致,其他大致都是相似的,所以我就简要的整理和汇总下:
1、互斥量与自解锁:
如下图所示:
qt的互斥量只有简单的QMutex,当然,某种程度上是std::thread里那几种结合体,自解锁也只有一种,如果不记得std::thread有哪些,请看我上一篇。用法比较简单,我就不贴代码了。
2、读写锁与其自解锁:
所谓读写锁,顾名思义,即读锁定状态与写锁定状态是不一样的。例如指定某段读取区域为lockForRead(),则表示这段代码仅仅是对资源进行读取,没有改变,所有线程可以共享该资源,无需阻塞;另一段改写某资源区域为LockForWrite(),则表示这段代码需要改写资源,其他线程需要阻塞,同时,lockForRead()锁定的读取资源代码也会被锁住,别的线程无论是读还是写会阻塞。
3、信号量:
图中其实把函数介绍的比较清楚了,即先指定一定数量的信号量,在实际用的过程中可用于一个类似生产者消费者模式,一个线程负责生产,如果信号量没有空位置了,rlease(加一个位置),另一个线程负责消费,如果有空位置,就消费一个(acquire)。
4、条件变量:
前面两个读写锁和信号量这些还和std::thread提供的机制有区别(因为没有,当然,想实现也可以用互斥量这些自己写一个)。条件变量几乎是没啥差异,用法也都差不多,函数已经在图中列出来了。
5、原子操作:
官网传送门:传送门
一共就如图所示只有三种,int,integer,pointer,用法也没太大的区别,不记得在上一篇有没有说过,原子对象本身也是带锁的,多线程访问的时候不用担心上锁问题,是为细粒度锁(mutex这些是粗粒度锁)。
6、future:
qfuture依赖于Qt Concurrent,下图接上图:
官网传送门:传送门
简单的说就是一个异步方法,类似std::async,只执行一次的异步方法,具体概念用法图中已经解释了。
下图解释下QFuture和QFutureWatcher关系(官网例子):
7、QThreadPool:
其实这个不算线程机制,不应该放上来,随便看看:
三、一张图:
该图能较好说明QThread的一些用法及生命周期。
如下图所示
参考资料:传送门
本次其实还有许多的东西可以说,只是做了个简单的图表,慢慢来,现在没有时间补充,总结完这些,其实理解上的话也比较简单,暂时就先这样,当做挖个坑吧= =。
========================= End
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2019-03-17 Kindle支持文档类型