Qt多线程使用的两种方式

这篇具有很好参考价值的文章主要介绍了Qt多线程使用的两种方式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Qt多线程两种方式:

方式1:继承自QThread类,重载run函数。此实现方法只有run()函数内的代码是运行在子线程内。(不使用事件循环)

使用方法
(1)run函数内有一个while或for的死循环(模拟耗时操作);

(2)通过一个标记位来控制死循环的退出;

(3)run()函数中无限循环记得强制休息,如果不加就会造成运行后CPU占用率100%的问题。一定要运行sleep将时间片让出去一下,处理其他事物;

(4)run()对于线程的作用相当于main函数对于应用程序。它是线程的入口,run的开始和结束意味着线程的开始和结束;

(5)在调用start()之后,新创建的线程就会调用run函数,默认实现调用exec()。

使用场景
适用于后台执行长时间的耗时操作,如文件复制、网络数据读取。

代码示例:

#ifndef QDEMOTHREAD_H
#define QDEMOTHREAD_H

#include <QThread>
#include <QDebug>

class QDemoThread : public QThread
{
    Q_OBJECT

public:
    QDemoThread(QObject* parent = nullptr);
    ~QDemoThread();

protected:
    void run() override;

public:
    void stop();

private:
    bool flag;
};
#endif // QDEMOTHREAD_H

#include "qdemothread.h"

QDemoThread::QDemoThread(QObject* parent) : QThread(parent)
{

}

QDemoThread::~QDemoThread()
{

}

void QDemoThread::run()
{
    flag = true;
    while(flag)
    {
        qDebug() << "thread id:" << QThread::currentThreadId();
        sleep(1);       //此处必须强制休息,否则CPU占用率很大
    }
}

void QDemoThread::stop()
{
    flag = false;
    if(isRunning())
    {
        exit();  // 结束线程
        wait();  // 等待退出
    }
}

方式二(推荐):
创建一个QThread和QWorker(继承自QObject)类对象,使用moveToThread函数移动到thread中运行,通过thread类start信号和worker的init槽函数绑定,init槽函数内是一些初始化操作,然后定义个定时器,周期触发doWork()。(使用事件循环)

使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。

网上有很多教程是在doWork()中使用while(isRunning)死循环的形式,不建议这么干,如果线程一直在doWork中死循环,那么他是无法接收到来自外部的信号的。推荐的方法是用定时器周期触发。

使用场景:
适用于事务性操作,如文件读写、数据库读写。

适合单次任务执行,即有点像懒人,触发一下,干一次活;

适合干完活,需要主动推送一个通知;

适合用于简化多线程中,对数据安全的保护。

不适用高频率任务,即跑完一个任务,可能没有时间休息,持续跑。

执行高频率任务,还是需要使用重写QThread::run()的方式,来实现。

代码示例:

QWorker类:

#ifndef QWORKER_H
#define QWORKER_H

#include <QObject>
#include <QThread>
#include <QDebug>

class QWorker : public QObject
{
    Q_OBJECT
public:
    explicit QWorker(QObject *parent = nullptr);
    ~QWorker();

signals:
    void newData(QByteArray data);    // 将本类内的私有数据通过该信号发射出去,供外部使用

public slots:
    void init();         // 一些必要的初始化操作写在此函数内
    void doWork();       // 一些耗时操作写在此函数内
    void writeData(const char* buf, qint64 len);  // 供外部使用的操作接口
};

#endif // QWORKER_H
#include "qworker.h"

QWorker::QWorker(QObject *parent) : QObject(parent)
{
    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}

QWorker::~QWorker()
{
    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}

void QWorker::init()
{
    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();
}

void QWorker::doWork()
{
    static int count = 0;
    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId() << ">>>" << count++;
}

void QWorker::writeData(const char* buf, qint64 len)
{
    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId() << ">>>" << QByteArray(buf, len);
}

主线程:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "qworker.h"
#include <QTimer>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QWorker* worker;
    QThread* thread;
    QTimer* timer;

signals:
    void writeData(const char* buf, qint64 len);
    void stopWork();
};

#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();

    timer = new QTimer(this);

    thread = new QThread();  // 不要指定parent
    worker = new QWorker();  // 不要指定parent

	// thread的finished和deleteLater相连接后,在thread退出时自动删除thread对象,无需手动delete thread
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); 
    // thread的finished和worker的deleteLater相连接后,在thread退出时自动删除worker对象,无需手动delete worker
    connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(started()), worker, SLOT(init()));

    connect(timer, SIGNAL(timeout()), worker, SLOT(doWork()));

    connect(this, SIGNAL(writeData(const char*,qint64)), worker, SLOT(writeData(const char*,qint64)));

    worker->moveToThread(thread);

    thread->start();

    timer->start(1000);  // 1000ms执行一次doWork()
}

MainWindow::~MainWindow()
{
    if(timer->isActive())
    {
        timer->stop();
    }

    if(thread->isRunning())
    {
        thread->quit();
        thread->wait();
    }

    qDebug() << __FUNCTION__ << "Thread ID:" << QThread::currentThreadId();

    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    emit writeData("hello world\r\n", 13);
}

二、QT线程同步

线程同步:Qt中使用多线程时候,多线程的同步就是一个不可避免的问题。

多线程的同步就是使多个线程在同时执行同一段代码的时候,有顺序的执行,不会出现同时有两个或者多个线程执行同一段代码的情况,特别是在对变量或者文件执行写操作的时候。也就是所谓的线程安全,线程安全指的是这段代码在一个线程或者多个线程执行的过程中,不加同步机制或者任何其他代码,执行的结果是一样的,这就是线程安全。

在Qt中常用的同步方法是使用锁机制,但是如果使用的方法不对或者时机不对同样也不会起到线程同步的效果。

例如: 有两个进程都对一个变量进行读写操作,这时就需要互斥量来进行线程同步,这个互斥量必须是全局的,不然如果各自在.cpp中声明一个QMutex mutex;是没有任何意义的。因为:每个线程对象都创建了一个QMutex对象,两个线程在运行过程中各自执行的是自己的锁,所以每个锁有且只有一个线程在执行。它们是互不干扰的,所有虽然加锁了,也没有起到同步的作用。

代码示例:

主程序:

// 多线程方式1
myThread = new MyThread();    // 继承自QThread
myThread->start();

// 多线程方式2
myThread1 = new MyThread1();    // 继承自QObject
thread1 = new QThread();
connect(thread1, SIGNAL(started(), myThread1 , SLOT(doWork)));
myThread1->moveToThread(thread1 );
thread1->start();

线程1:MyThread.cpp

#include "mythread.h"
#include <QMutex>

QMutex mutex;
extern int globalCount ;

MyThread::MyThread()
{
    isStop = true;
}

void MyThread::closeThread()
{
    isStop = true;
}
//重写基类的run函数
void MyThread::run()//只有run()里面在新的线程里
{
    qDebug()<<tr("mythread QThread::currentThreadId()==")<<QThread::currentThreadId();
    isStop = false;

//这种方式还是相当于在主线程里面执行
//    timer= new QTimer(NULL);
//    connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
//    timer->start(1000);
//    this->exec();
    while(1)
    {
        if(isStop)
        {
            return;
        }

        QMutexLocker locker(&mutex);
        qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();

        sleep(5);//QThread
    }
}

//这种方式还是相当于在主线程里面执行
void MyThread::onTimeout()
{
    qDebug()<<tr("mythread QThread::currentThreadId()==")<<QThread::currentThreadId();
    qDebug()<<"globalCount: " << ++globalCount;
}

线程2:MyThread1.cpp文章来源地址https://www.toymoban.com/news/detail-409226.html

#include "mythread1.h"
#include <QMutex>

extern QMutex mutex;
QMutex mutex1;
extern int globalCount ;

MyThread1::MyThread1(QObject *parent) : QObject(parent)
{
    isStop = true;
}

void MyThread1::closeThread()
{
    isStop = true;
}

void MyThread1::doWork()
{
    qDebug()<<tr("mythread2 QThread::currentThreadId()==")<<QThread::currentThreadId();
    isStop = false;

    //定时方式1:(都是在子线程中)
    timer= new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
    timer->start(1000);

    //定时方式2:(都是在子线程中)
//    while(1)
//    {
//        if(isStop)
//        {
//            return;
//        }

//        QMutexLocker locker(&mutex);
//        qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();

//        Sleep(1000);//<windows.h>,大写的S,单位是微秒
//    }
}

void MyThread1::onTimeout()
{
    QMutexLocker locker(&mutex);
    qDebug()<<"globalCount: " << ++globalCount <<QThread::currentThreadId();

}

到了这里,关于Qt多线程使用的两种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • QT打包的两种方式

    QT打包的两种方式: 一个是QT5自带的windeployqt(不需要下载安装),它可以找到程序(exe)用到的所有库文件,并且都拷贝到exe程序的当前文件。此时打包的exe较小,需要和拷贝进来的文件放一起运行,也可以将这些文件再次打包成一个大的exe文件,此时用的第二种打包方式

    2024年02月16日
    浏览(36)
  • 线程方法接收参数示例,Java的两种线程实现方式区别

    总所周知,Java实现多线程有两种方式,分别是继承Thread类和实现Runable接口,那么它们的区别是什么? 继承 Thread 类: 通过继承 Thread 类,你可以创建一个直接表示线程的类。你可以覆盖 Thread 类中的 run 方法来定义线程的逻辑。当调用 start 方法启动线程时,会执行该类中的

    2024年02月11日
    浏览(30)
  • 线程方法接收参数和返回参数,Java的两种线程实现方式对比

    总所周知,Java实现多线程有两种方式,分别是继承Thread类和实现Runable接口,那么它们的区别是什么? 继承 Thread 类: 通过继承 Thread 类,你可以创建一个直接表示线程的类。你可以覆盖 Thread 类中的 run 方法来定义线程的逻辑。当调用 start 方法启动线程时,会执行该类中的

    2024年02月11日
    浏览(31)
  • 【Qt】QThread & moveTothread-多线程的两种实现方法

    一、如何理解多线程 二、实现多线程的两种方式(面向应用) 2.1 继承 QThread 的类 2.2 (推荐这种方式)函数 moveTothread() 三、多线程的释放问题(善后工作) 类似我们单片机的编程,如在 Keil5 中对 51 单片机或者 STM32 单片机进行编程时,如果我们使用模块化编程,那么 main.c 文

    2024年02月01日
    浏览(34)
  • 【linux 多线程并发】线程本地数据存储的两种方式,每个线程可以有同名全局私有数据,以及两种方式的性能分析

    ​ 专栏内容 : 参天引擎内核架构 本专栏一起来聊聊参天引擎内核架构,以及如何实现多机的数据库节点的多读多写,与传统主备,MPP的区别,技术难点的分析,数据元数据同步,多主节点的情况下对故障容灾的支持。 手写数据库toadb 本专栏主要介绍如何从零开发,开发的

    2024年02月03日
    浏览(34)
  • Qt 测量文字展示尺寸的两种方式

    比如我想求字符串中最大的字符宽度,这个时候对每个字符的宽度求值肯定不是固定的,所以可以先用 MAXFLOAT 做约束,自适应宽度,代码如下: 某些情况下我想做文字的自动折行,指定了文本展示框的宽度之后,高度要通过折行策略达到自适应的效果:

    2024年02月06日
    浏览(43)
  • Qt中实现页面切换的两种方式

    在Qt中,可以使用QStackedWidget来实现两个UI界面的互相转换。QStackedWidget是一个堆叠窗口小部件,可以在其中添加多个子窗口,并且只显示其中一个子窗口。 注意:QStackedWidget只能用来装widget,不能装mainwindow!! ! 注意: 上面这种想法是错的! 下面这种想法才是对的! 注意

    2024年02月08日
    浏览(43)
  • QT操作excel的两种方式 QT基础入门【Excel的操作】

    QT操作excel的方式有两种:QAxObject 和QtXlsx QAxObject是通过调用office或者wps组件来实现对excel图表的操作的。只有装office软件或者wps软件就可以实现,但是 如果只装了office软件,有时可以用有时不可以用; 如果只装wps软件,完全不能使用; 如果两个都装了,基本上结果不可控;

    2024年02月12日
    浏览(24)
  • Qt Http请求,post和get的两种方式

    今天我们介绍一下QT的http请求,这里有post和get两种方式 一、post请求方式:参数分为网址和具体请求值两个部分,有时候还需要设置头信息,看具体情况定 二、get请求方式:参数和网址拼接到一起请求 三、主要区别: get用于请求获取数据,post向指定的资源创建修改数据 g

    2024年02月15日
    浏览(40)
  • pytorch使用gpu的两种方式

    在使用gpu进行训练或推理会比纯用cpu快好几倍,所以一般我们如果设备有gpu都尽量会用上gpu。 首先能使用gpu的有:数据(输入的图片、标注的label),损失函数,网络模型。 这三处都调用.cuda()进行返回。 网络模型: 损失函数: 数据(输入的图片、标注的label): 训练集、

    2024年02月16日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包