QT学习总结--实现多线程的两种方式

zhy

发布于 2020.03.31 20:02 阅读 2858 评论 0

qt实现多线程的方式大致分为两种:

     1. 直接继承自QThread,重写run函数

     2. 继承QObject,通过 moveToThread将事件添加到线程中处理

 

第一种方式:

 

#include "mythread.h"
#include <QDebug>
#include <QMutex>
 
MyThread::MyThread()
{
    isStop = false;
}
 
void MyThread::closeThread()
{
    isStop = true;
}
 
void MyThread::run()
{
    while (1)
    {
        if(isStop)
            return;
        qDebug()<<tr("mythread QThread::currentThreadId()==")<<QThread::currentThreadId();
        sleep(1);
    }
}

 

#include "widget.h"
#include <QDebug>
#include <windows.h>
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    createView();
}
 
void Widget::createView()
{
    /*添加界面*/    
    QPushButton *openThreadBtn = new QPushButton(tr("打开线程"));
    QPushButton *closeThreadBtn = new QPushButton(tr("关闭线程"));
    mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(openThreadBtn);
    mainLayout->addWidget(closeThreadBtn);
    mainLayout->addStretch();
    connect(openThreadBtn,SIGNAL(clicked(bool)),this,SLOT(openThreadBtnSlot()));
    connect(closeThreadBtn,SIGNAL(clicked(bool)),this,SLOT(closeThreadBtnSlot()));    
 
    /*线程初始化*/
    thread1 = new MyThread;
    connect(thread1,SIGNAL(finished()),this,SLOT(finishedThreadBtnSlot()));
}
 
void Widget::openThreadBtnSlot()
{
    /*开启一个线程*/    
    thread1->start();
    qDebug()<<"主线程id:"<<QThread::currentThreadId();
}
 
void Widget::closeThreadBtnSlot()
{
    /*关闭多线程*/
    thread1->closeThread();
    thread1->wait();
}
 
void Widget::finishedThreadBtnSlot()
{
    qDebug()<<tr("完成信号finished触发");
}
 
Widget::~Widget()
{
}

 

 

 

 

    采用第一种方法会有一个问题:重写run函数后,线程的默认事件循环不会启动,如果有信号从别的线程发送到该线程,那么该线程的槽函数并不会在这个线程中执行,而是由该线程所依赖的线程执行(一般是main线程),这导致了槽函数(slot)和run函数不在同一个线程,所以需要加 QMutex 锁住。

 

这样就很麻烦,这个问题有三个解决方案:

    1. 在自定义QThread中添加,moveToThread(this),这样槽函数就会在该线程中进行了。(这种方法不建议使用)

    2. 采用直接连接方式,即信号发出和接收都在自定义QThread中进行。

    3. 采用第二种方法:继承QObject类,通过moveToThread将事件添加到线程中处理。这种方法可以直接将槽函数与自定义QThread关联。

 

第二种方法:

 

#include "mythread.h"
#include <QDebug>
#include <QThread>
 
MyThread::MyThread(QObject *parent) : QObject(parent)
{
    isStop = false;
}
 
void MyThread::closeThread()
{
    isStop = true;
}
 
void MyThread::startThreadSlot()
{
    while (1)
    {
        if(isStop)
            return;
        qDebug()<<"MyThread::startThreadSlot QThread::currentThreadId()=="<<QThread::currentThreadId();
        QThread::sleep(1);
    }
}

 

#include <QDebug>
#include "widget.h"
 
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    createView();
}
 
Widget::~Widget()
{
}
 
void Widget::createView()
{
    /*UI界面*/
    mainLayout = new QVBoxLayout(this);
    QPushButton *openThreadBtn = new QPushButton(tr("打开线程"));
    QPushButton *closeThreadBtn = new QPushButton(tr("关闭线程"));
    mainLayout->addWidget(openThreadBtn);
    mainLayout->addWidget(closeThreadBtn);
    mainLayout->addStretch();
    connect(openThreadBtn,SIGNAL(clicked(bool)),this,SLOT(openThreadSlot()));
    connect(closeThreadBtn,SIGNAL(clicked(bool)),this,SLOT(closeThreadSlot()));
}
 
void Widget::openThreadSlot()
{
    /*开启一条多线程*/
    qDebug()<<tr("开启线程");
    firstThread = new QThread;                                                      //线程容器
    myObjectThread = new MyThread;
    myObjectThread->moveToThread(firstThread);                                      //将创建的对象移到线程容器中
    connect(firstThread,SIGNAL(finished()),myObjectThread,SLOT(deleteLater()));        //终止线程时要调用deleteLater槽函数
    connect(firstThread,SIGNAL(started()),myObjectThread,SLOT(startThreadSlot()));  //开启线程槽函数
    connect(firstThread,SIGNAL(finished()),this,SLOT(finishedThreadSlot()));
    firstThread->start();                                                           //开启多线程槽函数
    qDebug()<<"mainWidget QThread::currentThreadId()=="<<QThread::currentThreadId();
}
 
void Widget::closeThreadSlot()
{        
    qDebug()<<tr("关闭线程");
    if(firstThread->isRunning())
    {
        myObjectThread->closeThread();  //关闭线程槽函数
        firstThread->quit();            //退出事件循环
        firstThread->wait();            //释放线程槽函数资源
    }
}
 
void Widget::finishedThreadSlot()
{
    qDebug()<<tr("多线程触发了finished信号123");
}

 

   推荐用法:第二种方法 ,因为这种方法在使用信号和槽时根本不用考虑多线程的存在。也不用使用QMutex来进行同步,Qt的事件循环会自己自动处理好这个。总之就是很简单方便。