返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >QT5 Thread线程的具体实现
  • 637
分享到

QT5 Thread线程的具体实现

2024-04-02 19:04:59 637人浏览 泡泡鱼
摘要

目录一.首先分析一下 Qtimer Class与 Sleep()函数之间的秘密二.线程的引入;1.一个简单的控制台线程例子2.三个线程,自然会有优先权的问题,也就是cpu,先运行哪个

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(2000);
    Sleep(3000);  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();

最终显示结果为; 过时3秒制台显示hello world!lcd显示 1 再过时1秒显示2 再过2秒显示3 依次经过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 = 0; i < 1000; 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 = 6;

  void method1()
  {
      number *= 5;
      number /= 4;
  }

  void method2()
  {
      number *= 3;
      number /= 2;
  }

如果线程thread1 ,thread2分别顺序执行method1(),method2();最终结果将会是:

// method1()
  number *= 5;        // number is now 30
  number /= 4;        // number is now 7

  // method2()
  number *= 3;        // number is now 21
  number /= 2;        // number is now 10

number = 10;

但如果线程1在行动时,被系统挂载,或其它种种因素受到延时运行,比如有更高优先级线程申请运行,而线程2确并不受影响,最终结果将会是:

// Thread 1 calls method1()
  number *= 5;        // 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 *= 3;        // number is now 90
  number /= 2;        // number is now 45

  // Thread 1 finishes executing.
  number /= 4;        // number is now 11, instead of 10

此时number = 11; 并不等于10; 同一程序运行不同结果,这是不允许的

此时就要借助于QMutex 类;

QMutex mutex;
  int number = 6;

  void method1()
  {
      mutex.lock();
      number *= 5;
      number /= 4;
      mutex.unlock();
  }

  void method2()
  {
      mutex.lock();
      number *= 3;
      number /= 2;
      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 = 0);
    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 = 0; i < 100; i++)
    {
        QMutex mutex;
        mutex.lock();
        if(this->stop) break;
        mutex.unlock();
        emit sig(i);
        msleep(100);
    }
}
//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 = 0);
    ~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的错误应用。

class MyThread : public QThread
{
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(1000);  
       
        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线程的具体实现的文章就介绍到这了,更多相关QT5 Thread线程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: QT5 Thread线程的具体实现

本文链接: https://lsjlt.com/news/148841.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • QT5 Thread线程的具体实现
    目录一.首先分析一下 QTimer Class与 Sleep()函数之间的秘密二.线程的引入;1.一个简单的控制台线程例子2.三个线程,自然会有优先权的问题,也就是cpu,先运行哪个...
    99+
    2024-04-02
  • Qt5多线程编程的实现
    目录一、线程基础1、GUI线程与工作线程2、数据的同步访问二、QT多线程简介三、QThread线程四、简单实例一、线程基础 1、GUI线程与工作线程 每个程序启动后拥有的第一个线程称...
    99+
    2024-04-02
  • PythonpyechartsLine折线图的具体实现
    目录一、绘制折线图二、添加最小值最大值平均值三、竖线提示信息四、显示工具栏五、实心面积填充六、是否跳过空值七、折线光滑化八、多X轴九、阶梯图一、绘制折线图 import seabor...
    99+
    2024-04-02
  • java如何实现多线程Thread
    这篇文章将为大家详细讲解有关java如何实现多线程Thread,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。start()我们先来看看API中对于该方法的介绍:使该线程开始执行;Java 虚拟机调用该线程...
    99+
    2023-05-30
    java thread
  • 多线程Thread,Runnable,Callable实现方式
    目录一、创建线程的常用三种方式1、继承Thread类2、实现Runnable接口(重点)以多个线程并发,解决方法为例3、 实现Callable接口(JDK1.5版本之后引入的)总结一...
    99+
    2024-04-02
  • python中thread模块实现多线程
    这篇文章将为大家详细讲解有关python中thread模块实现多线程,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传统语言相比,Pyt...
    99+
    2023-06-14
  • Python threading和Thread模块及线程的实现
    目录前言1. 线程1.1 线程模块1.1.1 Thread类1.2 创建线程1.2.1 实例Thread类法创建线程1.2.1 继承重写Thread类法创建线程1.3 Join &a...
    99+
    2024-04-02
  • python thread模块怎么实现多线程
    这篇文章主要介绍“python thread模块怎么实现多线程”,在日常操作中,相信很多人在python thread模块怎么实现多线程问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”python thread...
    99+
    2023-06-30
  • Android 线程thread的两种实现方法(必看)
    这篇文章中有三点需要提前说明一下, 一:在android中有两种实现线程thread的方法: 一种是,扩展java.lang.Thread类 另一种是,实现Runnable接口...
    99+
    2022-06-06
    方法 Android
  • C++Thread实现简单的socket多线程通信
    目录起因服务端ROS客户端普通客户端运行效果不足起因 为什么要用C++的Thread,很简单,因为我菜 一打五用pthread实现了socket多线程通信,我之前学并发的时候没看pt...
    99+
    2024-04-02
  • Java如何通过继承Thread实现的线程类
    这篇文章主要为大家展示了“Java如何通过继承Thread实现的线程类”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java如何通过继承Thread实现的线程类...
    99+
    2024-04-02
  • Flex4 使用itemRenderer 为Tree加线具体实现
    要为Tree添加线,可以使用itemRenderer来自定义每个节点的显示样式。具体步骤如下:1. 创建一个自定义的itemRend...
    99+
    2023-08-14
    Flex4
  • GoStruct结构体的具体实现
    目录什么是结构体1. 基本实例化(方法1)2. new实例化(方法2)3. 键值对初始化(方法3 结构体能够使用指针就使用指针)结构体方法和接收者encoding-json包1. s...
    99+
    2023-03-15
    Go Struct结构体 Go Struct
  • Python多线程具体应用的方法
    本篇内容介绍了“Python多线程具体应用的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为了利用多核处理器,看过很多文档,研究很多py...
    99+
    2023-06-17
  • C++11中std::thread线程实现暂停(挂起)功能
    目录一、封装Thread类二、测试代码一、封装Thread类 我们基于C++11中与平台无关的线程类std::thread,封装Thread类,并提供start()、stop()、p...
    99+
    2023-05-17
    C++11 std::thread线程暂停 C++11 std::thread暂停
  • Qt5串口类QSerialPort的实现
    目录简述1.QSerialPortInfo类   2.QSerialPort类简述 在Qt5以上提供了QtSerialPort模块,方便编程人员快速的开发应用串口...
    99+
    2024-04-02
  • 怎么使用C++ Thread实现简单的socket多线程通信
    本篇内容介绍了“怎么使用C++ Thread实现简单的socket多线程通信”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!服务端多...
    99+
    2023-07-02
  • 基于QT5的文件读取程序的实现
    目录一、文件读写操作QFile1.1 头文件1.2 内部函数二、UI设计三、代码3.1 mainwindow.h3.2 mainwindow.c四、效果一、文件读写操作QFile Q...
    99+
    2024-04-02
  • 模板化编程的具体实现方式?
    模板化编程允许根据类型生成代码,提高可重用性和性能。它包括:在 c++++ 中使用模板指定类型参数,并通过实例化来生成代码。利用元编程在编译时操作类型信息,实现代码生成和静态分析等功能。...
    99+
    2024-05-08
    模板化编程 具体实现方式 c++
  • QT5实现简单的TCP通信的实现
    目录一、客户端二、服务器三、运行结果这段时间用到了QT的TCP通信,做了初步的学习与尝试,编写了一个客户端和服务器基于窗口通信的小例程。 使用QT的网络套接字需要.pro文件中加入一...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作