Qt多线程的几种实现方式
Qt多线程的实现方式有:
1. 继承QThread类,重写run()方法
2. 使用moveToThread将一个继承QObject的子类移至线程,内部槽函数均在线程中执行
3. 使用QThreadPool,搭配QRunnable(线程池)
4. 使用QtConcurrent(线程池)
为什么要用线程池?
创建和销毁线程需要和OS交互,少量线程影响不大,但是线程数量太大,势必会影响性能,使用线程池可以减少这种开销。
一、继承QThread类,重写run()方法
缺点:
1. 每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
2. 要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。
适用场景:QThread适用于那些常驻内存的任务。 1 //mythread.h 2 #ifndef MYTHREAD_H 3 #define MYTHREAD_H 4 5 #include 6 7 class MyThread : public QThread 8 { 9 public: 10 MyThread(); 11 void stop(); 12 13 protected: 14 void run(); 15 16 private: 17 volatile bool stopped; 18 }; 19 20 #endif // MYTHREAD_H 1 //mythread.cpp 2 #include "mythread.h" 3 #include 4 #include 5 6 MyThread::MyThread() 7 { 8 stopped = false; 9 } 10 11 12 13 void MyThread::stop() 14 { 15 stopped = true; 16 } 17 18 19 20 void MyThread::run() 21 { 22 qreal i = 0; 23 24 while( !stopped ) 25 { 26 qDebug() << QString("in MyThread: %1").arg(i); 27 sleep(1); 28 i++; 29 } 30 stopped = false; 31 } 1 //widget.h 2 #ifndef WIDGET_H 3 #define WIDGET_H 4 5 #include 6 #include "mythread.h" 7 8 9 QT_BEGIN_NAMESPACE 10 namespace Ui { class Widget; } 11 QT_END_NAMESPACE 12 13 class Widget : public QWidget 14 { 15 Q_OBJECT 16 17 public: 18 Widget(QWidget *parent = nullptr); 19 ~Widget(); 20 21 private slots: 22 void on_startBut_clicked(); 23 24 void on_stopBut_clicked(); 25 26 private: 27 Ui::Widget *ui; 28 MyThread thread; 29 }; 30 #endif // WIDGET_H 1 //widget.cpp 2 3 #include "widget.h" 4 #include "ui_widget.h" 5 6 Widget::Widget(QWidget *parent) 7 : QWidget(parent) 8 , ui(new Ui::Widget) 9 { 10 ui->setupUi(this); 11 ui->startBut->setEnabled(true); 12 ui->stopBut->setEnabled(false); 13 } 14 15 Widget::~Widget() 16 { 17 delete ui; 18 } 19 20 21 void Widget::on_startBut_clicked() 22 { 23 thread.start(); 24 ui->startBut->setEnabled(false); 25 ui->stopBut->setEnabled(true); 26 } 27 28 void Widget::on_stopBut_clicked() 29 { 30 if( thread.isRunning() ) 31 { 32 thread.stop(); 33 ui->startBut->setEnabled(true); 34 ui->stopBut->setEnabled(false); 35 } 36 }
二、使用moveToThread将一个继承QObject的子类移至线程
更加灵活,不需要继承QThread,不需要重写run方法,适用于复杂业务的实现。
注意,该业务类的不同槽函数均在同一个线程中执行。 1 //worker.h 2 #ifndef WORKER_H 3 #define WORKER_H 4 5 #include 6 7 class Worker : public QObject 8 { 9 Q_OBJECT 10 11 public: 12 Worker(); 13 14 ~Worker(); 15 16 public slots: 17 void doWork(); 18 19 void another(); 20 21 signals: 22 void stopWork(); 23 24 }; 25 26 #endif // WORKER_H 1 //worker.cpp 2 #include "worker.h" 3 #include 4 #include 5 6 Worker::Worker() 7 { 8 9 } 10 11 12 Worker::~Worker() 13 { 14 15 } 16 17 18 void Worker::doWork() 19 { 20 qDebug() << "current thread id is " << QThread::currentThreadId(); 21 emit stopWork(); 22 } 23 24 25 void Worker::another() 26 { 27 qDebug() << "another current thread id is " << QThread::currentThreadId(); 28 //emit stopWork(); 29 } 1 //dialog.h 2 #ifndef DIALOG_H 3 #define DIALOG_H 4 5 #include 6 #include 7 #include "worker.h" 8 9 QT_BEGIN_NAMESPACE 10 namespace Ui { class Dialog; } 11 QT_END_NAMESPACE 12 13 class Dialog : public QDialog 14 { 15 Q_OBJECT 16 17 public: 18 Dialog(QWidget *parent = nullptr); 19 ~Dialog(); 20 21 signals: 22 void startWork(); 23 void startAnother(); 24 25 public slots: 26 void endThread(); 27 28 private: 29 Ui::Dialog *ui; 30 QThread *m_pThread; 31 Worker *m_pWorker; 32 }; 33 #endif // DIALOG_H 1 //dialog.cpp 2 #include "dialog.h" 3 #include "ui_dialog.h" 4 #include 5 6 Dialog::Dialog(QWidget *parent) 7 : QDialog(parent) 8 , ui(new Ui::Dialog) 9 { 10 ui->setupUi(this); 11 12 m_pThread = new QThread(); 13 m_pWorker = new Worker(); 14 15 connect(this, &Dialog::startWork, m_pWorker, &Worker::doWork); 16 connect(this, &Dialog::startAnother, m_pWorker, &Worker::another); 17 connect(m_pWorker, &Worker::stopWork, this, &Dialog::endThread); 18 m_pWorker->moveToThread(m_pThread); 19 m_pThread->start(); 20 emit startWork(); 21 emit startAnother(); 22 } 23 24 Dialog::~Dialog() 25 { 26 delete ui; 27 delete m_pThread; 28 delete m_pWorker; 29 } 30 31 32 void Dialog::endThread() 33 { 34 qDebug() << "endThread"; 35 m_pThread->quit(); 36 m_pThread->wait(); 37 }
不过我为什么要用界面类呢?搞不懂!
三、使用QThreadPool,搭配QRunnable
QRunnable常用接口:
bool QRunnable::autoDelete() const;
void QRunnable::setAutoDelete( bool autoDelete); QRunnable 常用函数不多,主要设置其传到底给线程池后,是否需要自动析构; 若该值为false,则需要程序员手动析构,要注意内存泄漏;
QThreadPool常用接口:
void QThreadPool::start(QRunnable * runnable, int priority = 0);
bool QThreadPool::tryStart(QRunnable * runnable); start() 预定一个线程用于执行QRunnable接口,当预定的线程数量超出线程池的最大线程数后,QRunnable接口将会进入队列,等有空闲线程后,再执行; priority指定优先级 tryStart() 和 start() 的不同之处在于,当没有空闲线程后,不进入队列,返回false
业务类需要继承QRunnable,并且重写run()方法。注意,QRunnbale不是QObject的子类,可以发射信号,但用不了槽函数。
优点:无需手动释放资源,QThreadPool启动线程执行完成后会自动释放。
缺点:不能使用信号槽与外界通信。
适用场景:QRunnable适用于线程任务量比较大,需要频繁创建线程。QRunnable能有效减少内存开销 1 //myrunnable.h 2 #ifndef MYRUNNABLE_H 3 #define MYRUNNABLE_H 4 5 #include 6 #include 7 8 9 class MyRunnable : public QRunnable 10 { 11 public: 12 MyRunnable(const QString szThreadName); 13 void run(); 14 15 private: 16 QString m_szThreadName; 17 }; 18 19 #endif // MYRUNNABLE_H 1 //myrunnable.cpp 2 #include "myrunnable.h" 3 #include 4 #include 5 6 MyRunnable::MyRunnable(const QString szThreadName) : m_szThreadName(szThreadName) 7 { 8 9 } 10 11 12 void MyRunnable::run() 13 { 14 qDebug() << "Start thread id : " << QThread::currentThreadId(); 15 int iCount = 0; 16 17 while (1) 18 { 19 if(iCount >= 10) 20 { 21 break; 22 } 23 24 qDebug() << m_szThreadName << " count : " << iCount++; 25 QThread::msleep(500); 26 } 27 } 1 //mian.cpp 2 #include 3 #include "myrunnable.h" 4 #include 5 6 static QThreadPool* g_pThreadPool = NULL; 7 8 int main(int argc, char *argv[]) 9 { 10 QCoreApplication a(argc, argv); 11 12 MyRunnable* pRunnable1 = new MyRunnable("1# thread"); 13 pRunnable1->setAutoDelete(true); 14 15 MyRunnable* pRunnable2 = new MyRunnable("2# thread"); 16 pRunnable2->setAutoDelete(true); 17 18 g_pThreadPool = QThreadPool::globalInstance(); 19 20 g_pThreadPool->start(pRunnable1); 21 g_pThreadPool->start(pRunnable2); 22 23 g_pThreadPool = NULL; 24 25 return a.exec(); 26 }
四、使用QtConcurrent
Concurrent是并发的意思,QtConcurrent是一个命名空间,提供了一些高级的 API,使得在编写多线程的时候,无需使用低级线程原语,如读写锁,等待条件或信号。使用QtConcurrent编写的程序会根据可用的处理器内核数自动调整使用的线程数。这意味着今后编写的应用程序将在未来部署在多核系统上时继续扩展。
QtConcurrent::run能够方便快捷的将任务丢到子线程中去执行,无需继承任何类,也不需要重写函数,使用非常简单。
QtConcurrent常用接口:
QFuture< T> QtConcurrent::run(Function function, ...)
QFuture< T> QtConcurrent::run(QThreadPool *pool, Function function, ...)
需要在pro文件中添加: QT += concurrent 1 //main.cpp 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 static QThreadPool* g_pThreadPool = QThreadPool::globalInstance(); 10 11 class HELLO 12 { 13 public: 14 QString hello(QString szName) 15 { 16 qDebug() << "Hello " << szName << " from " << QThread::currentThreadId(); 17 return szName; 18 } 19 20 void run() 21 { 22 QFuture f3 = QtConcurrent::run(this, &HELLO::hello, QString("Lily")); 23 QFuture f4 = QtConcurrent::run(g_pThreadPool, this, &HELLO::hello, QString("Sam")); 24 25 f3.waitForFinished(); 26 f4.waitForFinished(); 27 28 qDebug() << "f3 : " << f3.result(); 29 qDebug() << "f4 : " << f4.result(); 30 } 31 }; 32 33 QString hello(QString szName) 34 { 35 qDebug() << "Hello " << szName << " from " << QThread::currentThreadId(); 36 return szName; 37 } 38 39 int main(int argc, char *argv[]) 40 { 41 QCoreApplication a(argc, argv); 42 43 QFuture f1 = QtConcurrent::run(hello, QString("Alice")); 44 QFuture f2 = QtConcurrent::run(g_pThreadPool, hello, QString("Bob")); 45 46 f1.waitForFinished(); 47 f2.waitForFinished(); 48 49 qDebug() << "f1 : " << f1.result(); 50 qDebug() << "f2 : " << f2.result(); 51 52 HELLO h; 53 h.run(); 54 55 g_pThreadPool = NULL; 56 57 return a.exec(); 58 }
探访乌镇数字孪生配电房科技赋能点亮智慧之光数字孪生配电房内部施紫楠摄中新网嘉兴11月3日电(记者施紫楠)配电房局放水平正常,运行电压电流及设备温度正常。3日,国网浙江省电力有限公司桐乡市供电公司(以下简称国网桐乡市供电公司
电科太极在京启动科技创新活动月中国日报11月3日(记者赵磊)记者从中国电子科技集团有限公司旗下中电太极(集团)有限公司获悉,以数智未来创新驱动为主题的电科太极科技创新活动月今天正式在京启动,启动仪式在中国电科1
第五届进博会将如期举办,哪些看点值得期待?第五届中国国际进口博览会将于11月5日至10日在上海举行。在已成功举办四届的基础上,此次进博会即将如期举办,释放了什么信号?同时又有哪些看点值得期待?如期举办释放什么信号?今年,面
个人养老金实施办法正式发布每年缴纳上限1。2万元视频加载中人力资源社会保障部财政部国家税务总局银保监会证监会11月4日联合发布了个人养老金实施办法,对个人养老金的参加流程资金账户管理机构与产品管理信息披露监督管理等做出具体规定。
11。3币圈大规模走强,可以关注下MASK和HFT美联储基准利率再次上调75个基点,至3。754区间,符合市场预期,对市场整体走势影响不大,但公布后鲍威尔的一系列言辞使得市场大跌眼镜,无论是美股还是加密市场均出现较严重的冲高回落,
聚和股份通过注册拟募资10亿刘海东为大股东雷递网雷建平11月4日常州聚和新材料股份有限公司(简称聚和股份)日前通过注册,准备在科创板上市。聚和新材计划募资10。27亿元。其中,2。73亿元用于常州聚和新材料股份有限公司年产
达芬奇投资牙科手术机器人文章来源MedRobot编辑唐子杰转载要求首发24小时后可转载,需注明来源2022年10月27日,美国牙科手术机器人公司Neocis宣布已完成最新一轮4000万美元(约2。9亿人民
碧桂园苏北区域总裁黄文锋非专业地产人士曾降职一个月后官复原职运营商财经网实习生孙彩霞文近期,头部房企碧桂园集团再次对其组织架构调整及人员变动,其中,任命黄文锋任命为苏北区域总裁,与他一同被任命的还有林恭博缪驰等。据悉,黄文锋8月份刚被降为副
阿斯利康从头回客到回头客每届进博会,阿斯利康展台都是医疗器械及医药保健展区关注度最高的展台之一。展台面积以及展品和活动的丰富度都位居前列。今年,阿斯利康将全面展现其在肿瘤心血管代谢呼吸消化等治疗领域的在华
珂尔谈规划,河南又谋划了一个金三角城镇群随着省级层面多个顶层规划的落地,河南新城镇空间格局基本确立。根据河南省新型城镇化规划(20212035年)来看,除了推动郑州都市圈扩容提质培育壮大副中心城市外,还将强化区域发展多极
阿富汗塔利班,从中巴声明中收到新信号,加入一带一路开始加速巴基斯坦总理谢里夫访华期间,中巴双方向外界发布了联合声明,声明中提到,中巴两国将继续向阿富汗提供人道主义和经济援助,同时加强两国的发展合作,推动中巴经济走廊向阿富汗延伸。中巴经济走
探访巴黎2024年奥运会和残奥会运动员村这是3月24日在法国巴黎北郊圣但尼拍摄的建设中的巴黎2024年奥运会和残奥会运动员村。新华社记者高静摄3月24日,媒体记者在位于法国巴黎北郊圣但尼的巴黎2024年奥运会和残奥会运动
永联杯全国拔河城市联赛(宁夏站)在银川激情开赛来源银川新闻网3月25日,永联杯全国拔河城市联赛(宁夏站)在宁夏银川激情开赛,共有来自全区各市县(区)及自治区体育局系统的26支代表队300余人汇聚凤城,以一根绳为纽带,展示力量,
哪个品牌工装裤好,有什么推荐?大家好,这里是怪人聚集地。据说,关注我的人都越来越帅了。说实话,男人十有八九都爱工装曾几何时,工装这件专为工人阶级所设计的风格服饰,现跟随各种复古风回潮带来了更多粗犷与荷尔蒙爆棚的
五十的人了,去南方打工还有人要吗,工资五六千?你好,很高兴回答这个问题。50的人了,去南方打工还有人要吗?工资五六千?当然有人要,但是工资五六千的工作选择性不多,下面我就给你推荐几个工作。1如果你没技术的话,可以到建筑工地当小
农村赡养父母,流行儿女均摊,这样做真的公平吗?6公平个屁,不知道这是谁兴起的。我的农村人,大多数农村人对待儿子就是宝贝,对待女儿就像狗屎。儿子从小到大在家里说一不二,最好的都给他,活儿都是女孩干,然后女孩要打工挣钱给家里男孩娶
津琴科加盟阿森纳后以我看到的一切,我觉得我们不必考虑争四直播吧3月24日讯国际比赛日之前,阿森纳暂时以8分的优势领跑英超积分榜,这意味着他们很有可能夺得本赛季的联赛冠军。在接受每日邮报专访时,阿森纳后卫津琴科谈论了有关阿森纳以及争冠的相
媒体人足协准入与案件无关涉案球员官员要等宣判后进行处罚直播吧3月25日讯据媒体人李璇报道,当前足协对于新赛季的准入工作,与相关案件没有任何关系。中国足协竞赛部部长黄松涉嫌严重违纪违法,目前正接受中央纪委国家监委驻国家体育总局纪检监察组
最新排名!中国女足世界排名再升1位,恭喜水庆霞,陈戌源惭愧!北京时间3月25日消息,众所周知,中国女足正在积极备战世界杯比赛,球队目前在水庆霞的指导下整体气势不错。中国女足承载着中国足球人的希望,因为中国男足的所作所为已经伤透了国人的心。球
中国队主场打赢澳大利亚队还有多大机会晋级世界杯?中国队主场打赢澳大利亚队的话,还有机会晋级世界杯!还必须打赢日本队,沙特队,阿曼队,越南队!必须5连胜!因为前期己打的5场比赛,中国队输给了澳大利亚,沙特和日本,只赢了越南平了阿曼
王艺迪23负于倪夏莲,是轻敌吗?不属于轻敌主要原因如下首先,倪夏莲作为原国乒老将,不仅身经百战,经验丰富而且打法独特,一面正胶,一面长胶正胶击球快速,长胶怪异大迪遭遇这样一位难缠的对手,极不适应这是输球的主要因素
助听器取下来听力会有变化吗?你好,听力有损失戴上助听器后能够提升听力,与人沟通交流变得轻松,生活质量得以提高,取下来后听力恢复到没有戴的状态,沟通交流变得困难,会觉得不适应,只要不是身体疾病原因,取下助听器是