QT5 Thread线程
QT5 Thread线程继承QThread方式
一.首先分析一下 QTimer Class与 Sleep()函数之间的秘密
QTimer *t = new QTimer(*parent); //创建QTimer 对象
t->start(_time); //计时开始每隔_time时间自动触发&QTimer::timeout信号
t->stop(); //结束计时
Sleep() //windows.h里面的系统延时函数
通过以上方法实现案例:
//button 槽函数
void Widget::on_buttonstart_clicked()
{
t->start();
Sleep();
qDebug() << "hello world!";
}
//timeout信号处理函数
connect(t, &QTimer::timeout,
[=]()
{
ui->lcd_1->display(++i);
});
分析,在没有Sleep()函数的情况下:
点击开始立马在控制台显示hello world!;每隔2秒lcd显示+1;
有Sleep()的存在后;点击开始程序本质是想每隔2秒lcd显示+1;3秒后控制台显示hello world!;
最终结果是:
点击开始,计时器计时,2秒后,不运行connect();3秒后connect()第一次运行;再过4秒,第二次timeout信号触发,再次运行connect();
最终显示结果为; 过时秒制台显示hello world!lcd显示 再过时1秒显示 再过秒显示 依次经过2秒显示累加1;
二.线程的引入;
如果我们想要的结果是,点击按钮,lcd每一秒显示+1, 3秒控制台回显hello world! 也就是Sleep(3000)显示hello world!并不会去影响到Qtrimer计时;
单独创建线程A,在A线程是实现延时3秒输出hello world!;
1.一个简单的控制台线程例子
新建一个qt控制台程序 自定义一个类 这里就叫class mythread
//mythread.h #ifndef MYTHREAD_H
#define MYTHREAD_H #include <QThread> class myThread: public QThread
{
public:
myThread();
void run(); //声明继承于QThread虚函数 run()
}; #endif // MYTHREAD_H
//mythread.cpp #include "mythread.h"
#include <QDebug> myThread::myThread()
{ }
void myThread::run()
{
qDebug() << "hello world!"; //复写QThread类的 run()函数
}
//main.cpp
#include <QCoreApplication>
#include "mythread.h" //包涵头文件
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); myThread *thread1 = new myThread; //新建线程对象
thread1->start(); //启动线程 return a.exec();
}
上例启动了一个新线程中输出hello world!
改进上例:
//mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H #include <QThread> class myThread: public QThread
{
public:
myThread();
void run();
QString name; //添加一个 name 对象
}; #endif // MYTHREAD_H
//mythread.cpp
#include "mythread.h"
#include <QDebug> myThread::myThread()
{ }
void myThread::run()
{
qDebug() << this->name << "hello world!";
//添加一个for循环
for(int i = ; i < ; i++)
{
qDebug() << this->name << i;
}
}
//main.cpp #include <QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); //连续创建三个子线程
myThread *thread1 = new myThread;
thread1->name = "mythred1";
thread1->start(); myThread *thread2 = new myThread;
thread2->name = "mythred2";
thread2->start(); myThread *thread3 = new myThread;
thread3->name = "mythred3";
thread3->start(); return a.exec();
}
运行结果:
结果显示输出为无序输出,结论三个线程完全独立运行,互不影响;
2.三个线程,自然会有优先权的问题,也就是cpu,先运行哪个线程;下面让我们来谈谈优先权
线程权限由线程启动函数start(Priority枚举)控制
如上例:在启动函数中加入枚枚量,具体参数可查帮助文档:
3.QMutex 类
QMutex类提供了线程之间的访问序列化。
QMutex的目的是保护对象,数据结构或代码段,以便一次只有一个线程可以访问它(这与Java synchronized关键字类似)。 QMutexLocker通常最好使用互斥锁,因为这样可以很容易地确保锁定和解锁一致地执行。
int number = ; void method1()
{
number *= ;
number /= ;
} void method2()
{
number *= ;
number /= ;
}
如果线程thread1 ,thread2分别顺序执行method1(),method2();最终结果将会是:
// method1()
number *= ; // number is now 30
number /= ; // number is now 7 // method2()
number *= ; // number is now 21
number /= ; // number is now 10
number = 10;
但如果线程1在行动时,被系统挂载,或其它种种因素受到延时运行,比如有更高优先级线程申请运行,而线程2确并不受影响,最终结果将会是:
// Thread 1 calls method1()
number *= ; // number is now 30 // Thread 2 calls method2().
//
// Most likely Thread 1 has been put to sleep by the operating
// system to allow Thread 2 to run.
number *= ; // number is now 90
number /= ; // number is now 45 // Thread 1 finishes executing.
number /= ; // number is now 11, instead of 10
此时number = 11; 并不等于10; 同一程序运行不同结果,这是不允许的
此时就要借助于QMutex 类;
QMutex mutex;
int number = ; void method1()
{
mutex.lock();
number *= ;
number /= ;
mutex.unlock();
} void method2()
{
mutex.lock();
number *= ;
number /= ;
mutex.unlock();
}
当你在一个线程中调用lock()时,其他线程会试图在同一个地方调用lock(),直到获得锁的线程调用unlock()。 lock()的一个非阻塞替代是tryLock()。
QMutex在非竞争情况下进行了优化。 如果该互斥体没有争用,则非递归QMutex将不分配内存。 它的构建和销毁几乎没有开销,这意味着有很多互斥体作为其他类的一部分是很好的。
当线程1被cpu延时处理,而线程2处理到method2()时自动会进入method1()继续处理number /=4;再回到method2();而此时如果线程1继续执行时,自动又会进入到method2();
4.QThread 启动暂停等待信号与槽控制实例
延续控制台线程例子 在每个线程后面加上 thread1->wait(); qDebug() << "hello world!";
预期的结果将会是, 在线程输出完后才会输出hello world!
#include <QCoreApplication>
#include "mythread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); //连续创建三个子线程
myThread *thread1 = new myThread;
thread1->name = "mythred1";
thread1->start(); thread1->wait();
qDebug() << "hello world!"; return exec();
}
现在转到GUI下,下面一个例子:
//自定义线程类,头文件
#ifndef NITHREAD_H
#define NITHREAD_H #include <QThread> class nithread : public QThread
{
Q_OBJECT
public:
explicit nithread(QObject *parent = );
bool stop; signals:
void sig(int); protected:
void run(); public slots:
}; #endif // NITHREAD_H
//自定义线程类cpp
#include "nithread.h"
#include <QMutex>
nithread::nithread(QObject *parent) : QThread(parent)
{ } void nithread::run()
{
for(int i = ; i < ; i++)
{
QMutex mutex;
mutex.lock();
if(this->stop) break;
mutex.unlock();
emit sig(i);
msleep();
}
}
//GUi窗口类头文件
#ifndef DIALOG_H
#define DIALOG_H #include <QDialog>
#include <nithread.h> namespace Ui {
class Dialog;
} class Dialog : public QDialog
{
Q_OBJECT public:
explicit Dialog(QWidget *parent = );
~Dialog(); private slots:
void on_buttonstart_clicked();
void lot(int); void on_buttonstop_clicked(); private:
Ui::Dialog *ui;
nithread *threadd;
}; #endif // DIALOG_H
//GUI类cpp
#include "dialog.h"
#include "ui_dialog.h" Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
threadd = new nithread(this);
connect(threadd, SIGNAL(sig(int)), this, SLOT(lot(int)));
} Dialog::~Dialog()
{
delete ui;
} void Dialog::on_buttonstart_clicked()
{
threadd->start();
} void Dialog::lot(int num)
{
ui->numberlabel->setText(QString::number(num));
} void Dialog::on_buttonstop_clicked()
{
threadd->stop = true;
}
//main.cpp
#include "dialog.h"
#include <QApplication> int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show(); return a.exec();
}
最终结果:
当点击start 开启线程 stop 停止线程 通过显号与槽显示结果
然而方法一Thread线程继承QThread方式,在实际问题中却有着很多的问题如下文简介:早在2006年已经被qt工程师提出;(更直指此方法是错误的用法)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我们(Qt用户)正广泛地使用IRC来进行交流。我在Freenode网站挂出了#qt标签,用于帮助大家解答问题。我经常看到的一个问题(这让我不厌其烦),是关于理解Qt的线程机制以及如何让他们写的相关代码正确工作。人们贴出他们的代码,或者用代码写的范例,而我则总是以这样的感触告终:
你们都用错了!
我觉得有件重要的事情得澄清一下,也许有点唐突了,然而,我不得不指出,下面的这个(假想中的)类是对面向对象原则的错误应用,同样也是对Qt的错误应用。
{
public:
MyThread()
{
moveToThread(this);
}
void run();
signals:
void progress(int);
void dataReady(QByteArray);
public slots:
void doWork();
void timeoutHandler();
};
我对这份代码最大的质疑在于 moveToThread(this); 我见过太多人这么使用,并且完全不明白它做了些什么。那么你会问,它究竟做了什么?moveToThread()函数通知Qt准备好事件处理程序,让扩展的信号(signal)和槽(slot)在指定线程的作用域中调用。QThread是线程的接口,所以我们是在告诉这个线程在“它内部”执行代码。我们也应该在线程运行之前做这些事。即使这份代码看起来可以运行,但它很混乱,并不是QThread设计中的用法(QThread中写的所有函数都应该在创建它的线程中调用,而不是QThread开启的线程)。
在我的印象中,moveToThread(this); 是因为人们在某些文章中看到并且使用而流传开来的。一次快速的网络搜索就能找到此类文章,所有这些文章中都有类似如下情形的段落:
- 继承QThread类
- 添加用来进行工作的信号和槽
- 测试代码,发现槽函数并没有在“正确的线程”中执行
- 谷歌一下,发现了moveToThread(this); 然后写上“看起来的确管用,所以我加上了这行代码”
我认为,这些都源于第一步。QThread是被设计来作为一个操作系统线程的接口和控制点,而不是用来写入你想在线程里执行的代码的地方。我们(面向对象程序员)编写子类,是因为我们想扩充或者特化基类中的功能。我唯一想到的继承QThread类的合理原因,是添加QThread中不包含的功能,比如,也许可以提供一个内存指针来作为线程的堆栈,或者可以添加实时的接口和支持。用于下载文件、查询数据库,或者做任何其他操作的代码都不应该被加入到QThread的子类中;它应该被封装在它自己的对象中。
通常,你可以简单地把类从继承QThread改为继承QObject,并且,也许得修改下类名。QThread类提供了start()信号,你可以将它连接到你需要的地方来进行初始化操作。为了让你的代码实际运行在新线程的作用域中,你需要实例化一个QThread对象,并且使用moveToThread()函数将你的对象分配给它。你同过moveToThread()来告诉Qt将你的代码运行在特定线程的作用域中,让线程接口和代码对象分离。如果需要的话,现在你可以将一个类的多个对象分配到一个线程中,或者将多个类的多个对象分配到一个线程。换句话说,将一个实例与一个线程绑定并不是必须的。
我已经听到了许多关于编写Qt多线程代码时过于复杂的抱怨。原始的QThread类是抽象类,所以必须进行继承。但到了Qt4.4不再如此,因为QThread::run()有了一个默认的实现。在之前,唯一使用QThread的方式就是继承。有了线程关联性的支持,和信号槽连接机制的扩展,我们有了一种更为便利地使用线程的方式。我们喜欢便利,我们想使用它。不幸的是,我太晚地意识到之前迫使人们继承QThread的做法让新的方式更难普及。
我也听到了一些抱怨,是关于没有同步更新范例程序和文档来向人们展示如何用最不令人头疼的方式便利地进行开发的。如今,我能引用的最佳的资源是我数年前写的一篇博客。()
免责声明:你所看到的上面的一切,当然都只是个人观点。我在这些类上面花费了很多精力,因此关于要如何使用和不要如何使用它们,我有着相当清晰的想法。
译者注:
最新的Qt帮助文档同时提供了建立QThread实例和继承QThread的两种多线程实现方式。根据文档描述和范例代码来看,若想在子线程中使用信号槽机制,应使用分别建立QThread和对象实例的方式;若只是单纯想用子线程运行阻塞式函数,则可继承QThread并重写QThread::run()函数。
由于继承QThread后,必须在QThread::run()函数中显示调用QThread::exec()来提供对消息循环机制的支持,而QThread::exec()本身会阻塞调用方线程,因此对于需要在子线程中使用信号槽机制的情况,并不推荐使用继承QThread的形式,否则程序编写会较为复杂。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
从Qt4.4开始,可以采用新的方法也是被称为正确的方法也是qt想推广的方法:
// Worker 类定义 cpp
#include <QtCore>
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
}
};
//main函数cpp int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"From main thread: "<<QThread::currentThreadId(); QThread t;
QTimer timer;
Worker worker; QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(); worker.moveToThread(&t); t.start(); return a.exec();
}
总结:
继承QThread老式方法
1.定义继承QThread的类A 复写run()函数;
2.在主线程中实例化A对象a
3.通过调用a->start()启动线程,线程会自动调用run()虚函数;run不可直接调用;
新方法:
1.创建继承Obeject的类A 将要在线程中实现的方法在A类中实现
2.在主线程中实例化A对象a,再实例化QThread类对象b
3.通过a.moveToThread(&b);将a对象的实现移入线程b对象作用范围内运行
4.b->start()启动线程;
5.通过信号与槽的方式启动调用A类成员函数;
常用函数:
QThread类
start(),//启动线程;
wait()//等待线程运行结束;
quit(),//线程运行结束退出线程
线程与进程区别:
进程是系统为每个程序分配有独立运行空间的运行实例
线程是与进程共用内存空间的一个独立运行实例;相对而言线程比进程的消耗更低;
结语:
新版qt5的主要目的也就是让每个线程能独立运行在其线程作用域中,线程与线程之前的交互则通过connect机制;因此对于需要在子线程中使用信号槽机制的情况,并不推荐使用继承QThread的形式;些方式仅实用于在只需要在run()中运行一些简单的函数;
QT5 Thread线程的更多相关文章
- 学习接水系统(java+thread线程)
(一)项目框架分析 对于学生并发接水项目,根据面向对象的思想,需要创建两个对象,即学生和水龙头. 接下来主要讲解不排队接水和排队接水两张情况. 项目的目录文件如下: (二)不排队接水 假设有四个学生小 ...
- Thread线程join方法自我理解
Thread线程join方法自我理解 thread.join():等待thread线程运行终止,指的是main-thread(main线程)必须等待thread线程运行结束,才能继续thread.jo ...
- java 并发(三)---Thread 线程
Thread 的状态 线程共有五种状态.分别是: (1)新建 (2)就绪 (3)运行 (4)阻塞 (5)死亡 ,下面列列举的状态需要结合状态示意图更好理解. 新建状态(New): 新创建了一个线程对 ...
- c++11中关于`std::thread`线程传参的思考
关于std::thread线程传参的思考 最重要要记住的一点是:参数要拷贝到线程独立内存中,不管是普通类型.还是引用类型. 对于传递参数是引用类型,需要注意: 1.当指向动态变量的指针(char *) ...
- Thread 线程池
Thread 线程池: 当使用多个较短存活期的线程有利时,运用线程池技术可以发挥作用.运用这一技术时,不是为每个任务创建一个全新的线程,而可以从线程池中抽出线程,并分配给任务.当线程完成任务后,再把它 ...
- Thread线程的基础知识及常见疑惑点
引言 相信各位道友在平时工作中已经很少直接用到Thread线程类了,现在大多是通过线程池或者一些多线程框架来操作线程任务,但我觉得还是有必要了解清楚Thread线程类中各种方法的含义,了解了底层才能更 ...
- Thread线程框架学习
原文:https://www.cnblogs.com/wangkeqin/p/9351299.html Thread线程框架 线程定义:线程可以理解为一个特立独行的函数.其存在的意义,就是并行,避免了 ...
- std::thread线程库详解(2)
目录 目录 简介 最基本的锁 std::mutex 使用 方法和属性 递归锁 std::recursive_mutex 共享锁 std::shared_mutex (C++17) 带超时的锁 总结 简 ...
- Thread线程控制之sleep、join、setDaemon方法的用处
Thread线程控制之sleep.join.setDaemon方法的用处 1. sleep方法 public static void sleep(long millis) throws Interru ...
随机推荐
- SLAM入门之视觉里程计(4):基础矩阵的估计
在上篇文章中,介绍了三位场景中的同一个三维点在不同视角下的像点存在着一种约束关系:对极约束,基础矩阵是这种约束关系的代数表示,并且这种约束关系独立与场景的结构,只依赖与相机的内参和外参(相对位姿).这 ...
- Xamarin~Android篇~监听返回键,单击返回某个webView,双击退出
https://www.cnblogs.com/lori/p/5088627.html DateTime? lastBackKeyDownTime; public override bool OnKe ...
- Jupyter Notebook使用小技巧
在 C:\Windows\Fonts目录下找到Mircosoft YaHei UI字体,然后复制到[你的Python安装路径]/Lib/site-packages/matplotlib/mpl-dat ...
- SQL基础学习_05_函数、谓词、CASE表达式
函数 算术函数 1. 四则运算: +.-.*./ 2. ABS:求绝对值, ABS(数值) 3. MOD: 求余,MOD(被除数,除数) 4. ROUND:四舍五入,ROUND(对象数值,保留小数的 ...
- async await promise
async 异步函数,以后可能会用得很广. 1.箭头函数: 没有{ }时不写return 也有返回值 2.Promise : 异步神器,很多异步api都是基于Promise 3.new Promise ...
- 走进Vue时代进阶篇(01):重构电商购物车模块
前言 从这篇文章开始,我准备给大家分享一些关于Vue.js这门框架的技巧性系列文章,正好我们公司项目中也用到了Vue.所以,教是最好的学.进阶篇比较适合于二三线城市,还在小厂打拼的童鞋们.欢迎你们跟着 ...
- 什么是ObjCTypes?
先看一下消息转发流程: 在forwardInvocation这一步,你必须要实现一个方法: - (NSMethodSignature *)methodSignatureForSelector:(SEL ...
- 计算机基础理论知识梳理篇(三):VLAN与VLAN网卡相关概念
VLAN(Virtual Local Area Network) 虚拟局域网(VLAN,802.1Q)是一组逻辑上的设备和用户,这些设备和用户并不受物理位置的限制,可以根据功能.部门及应用等因素将它们 ...
- sql基本知识
1.类型转换 用convert,cast float转换出现科学计数字母的问题:可以先转成numeric再转成varchar declare @fl float set @fl=1234567.123 ...
- 设计模式(三)装饰者模式Decorator
装饰者模式针对的问题是:对一个结构已经确定的类,在不改变该类的结构的情况下,动态增加一些功能. 一般来说,都是对一些已经写好的架构增加自己的功能,或者应对多种情况,增加功能. 我们还是来玩一句红警,首 ...