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 
#include 

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 
#include 
#include 

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 
#include "qworker.h"
#include 

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 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

#include "mythread1.h"
#include 

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 <

//        Sleep(1000);//,大写的S,单位是微秒
//    }
}

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

}

你可能感兴趣的:(Qt实战---高级,qt,ui,开发语言)