29初识线程2

前一节介绍了线程的创建,把一个线程单独作为一类且是继承Qthread,当中也只有一个线程处理函数。很大的弊端。

  1. 规定了继承Qthread类,若要使用QWidget等其他基类呢?
  2. 线程处理函数run(),只能重写,不可以自定义。

因此,在Qt4.7及以后版本推荐使用以下的工作方式。其主要特点就是利用Qt的事件驱动特性,将需要在子线程中处理的业务放在独立的模块(类)中,由主线程创建完该对象后,将其移交给指定的线程,且可以将多个类似的对象移交给同一个线程。

换句话说,就是新建一个类MyThread,该类不是继承Qthread类,但必须继承QObject。将MyThread当成一个普通的类非线程类。再在窗体使用Qthread类来创建子线程对象,将类MyThread附加到子线程Qthread。(MyThread就是乘客,Qthread就是汽车)。

问题一:为什么自定义类不可以指定父对象?

void QObject::moveToThread(QThread * targetThread)

在调用moveToThread,若自定义类指定了父对象,而Qthread是有父对象的,moveToThread不允许使用。

问题二:为什么要使用signal-slot,而不直接调用线程处理函数。

直接调用,导致会导致线程处理函数和主线程是在同一个线程中。

NewThread->MySlot()

使用signal-slot来调用,主线程和子线会独立。

 

多线程使用过程中注意事项:

   线程不能操作UI对象(从Qwidget直接或间接派生的窗口对象)

   需要移动到子线程中处理的模块类,创建的对象的时候不能指定父对象。

结果图:

源代码:

自定义线程部分

newthread.h

#ifndef NEWTHREAD_H

#define NEWTHREAD_H

#include <QObject>

class NewThread : public QObject

{

    Q_OBJECT

public:

    explicit NewThread(QObject *parent = 0);

public:

    void MySlot();  //自定义线程处理函数

signals:

    void MySignal();//自定义信号

public slots:

};

#endif // NEWTHREAD_H

newthread.cpp

#include "newthread.h"

#include <QThread>

#include <QDebug>

NewThread::NewThread(QObject *parent) :

    QObject(parent)

{

}

void NewThread::MySlot()

{

    QThread::sleep(5);

    //操作完成,发出信号

    emit MySignal();

    qDebug()<<"子线程ID:"<<QThread::currentThreadId();

}

主线程部分

widget.h

#ifndef WIDGET_H

#define WIDGET_H

#include <QWidget>

#include <QTimer>

#include "newthread.h"

#include <QThread>

namespace Ui {

class Widget;

}

class Widget : public QWidget

{

    Q_OBJECT

public:

    explicit Widget(QWidget *parent = 0);

    ~Widget();

signals:

    void threadSignal();    //提示去调用线程处理函数

private slots:

    void on_pushButton_clicked();   //开始按钮

    void on_pushButton_2_clicked(); //暂停按钮

private:

    Ui::Widget *ui;

    QTimer *timer;          //定时器

    NewThread *myThread;    //自定义线程对象

    QThread  *thread;       //子线程

};

#endif // WIDGET_H

widget.cpp

#include "widget.h"

#include "ui_widget.h"

#include <QThread>

#include <QDebug>

Widget::Widget(QWidget *parent) :

    QWidget(parent),

    ui(new Ui::Widget)

{

    ui->setupUi(this);

    //自定义线程对象,不可以指定父对象

    myThread =new NewThread;

    //创建子线程

    thread =new QThread(this);

    //将自定义线程对象附加到子线程

    myThread->moveToThread(thread);

    timer=new QTimer(this);

    //定时器信号

    connect(timer,&QTimer::timeout,

            [=]()

            {

               static int num=0;

               ui->lcdNumber->display(num);

               num++;

            }

    );

    //调用线程处理函数

    connect(this,&Widget::threadSignal,myThread,&NewThread::MySlot);

    //接受子线程发出信号

    connect(myThread,&NewThread::MySignal,

            [=]()

            {

                qDebug()<<"over";

                timer->stop();

            }

            );

   connect(this,&QWidget::destroyed,

           [=]()

           {

                thread->quit();

                thread->wait();

           }

   );

}

Widget::~Widget()

{

    delete ui;

}

void Widget::on_pushButton_clicked()

{

    //开始定时器

    if(timer->isActive()!=true)

        timer->start(1000);

    //开始线程,但不启动线程处理函数

    thread->start();

    //发出信号,提示去调用线程处理函数

    emit threadSignal();

    //myThread->MySlot();

    qDebug()<<"主线程ID:"<<QThread::currentThreadId();

}

void Widget::on_pushButton_2_clicked()

{

    if(timer->isActive()==true)

        timer->stop();

}

补充:

关于Qobject类的connect函数最后一个参数,连接类型:

   自动连接(AutoConnection),默认的连接方式。

   如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;

   如果发送者与接受者处在不同线程,等同于队列连接。

   直接连接(DirectConnection)

当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行。

   队列连接(QueuedConnection)

当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行。

总结:

* 队列连接:槽函数在接受者所在线程执行。

* 直接连接:槽函数在发送者所在线程执行。

* 自动连接:二者不在同一线程时,等同于队列连接

原文地址:https://www.cnblogs.com/gd-luojialin/p/9215823.html