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

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

    总结:

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

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

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

  • 相关阅读:
    OBS直播和相关操作
    计算机信息安全易错题目整理3
    计算机信息安全易错题目整理1
    LeetCode SQL题目(第一弹)
    操作系统及安全复习笔记
    网络安全web部分
    《算法分析与设计基础》考前复习提纲
    兼容新旧浏览器的flex写法
    特效背景粒子插件particles.js
    前端工程师必备的几个实用网站
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/9215823.html
Copyright © 2020-2023  润新知