• 28初识线程


    通常情况下,应用程序都是在一个线程中执行操作。但是,当调用一个耗时操作(例如,大批量I/O或大量矩阵变换等CPU密集操作)时,用户界面常常会冻结。而使用多线程可以解决这一问题。

    多线程有以下几个优势:

    1.提高应用程序响应速度。这对于图形界面开发的程序尤为重要,当一个操作耗时很长时,整个系统都会等待这个操作,程序就不能响应键盘、鼠标、菜单等操作,而使用多线程技术可将耗时长的操作置于一个新的线程,避免以上问题。

    2.使多CPU系统更加有效。当前线程数不大于CPU数目时,操作系统可以调度不同的线程运行于不同的CPU上。

    3.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为独立或半独立的运行部分,这样有利于代码的理解和维护。

    多线程程序有以下几个特点:

    1.多线程程序的行为无法预期,当多次执行程序时,每一次的结果都可能不同。

    2.多线程的执行顺序无法保证,它与操作系统的调度策略和线程优先级等因素有关。

    3.多线程的切换可能发生在任何时刻、任何地点。

    4.多线程对代码的敏感度高,对代码的细微修改都可能产生意想不到的结果。

    基于以上这些特点,为了有效的使用线程,开发人员必须对其进行控制。

    例子:

    //开始按钮

    void Widget::on_pushButton_clicked()

    {

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

            timer->start(100);

        QThread::sleep(5);

    }

    前面介绍了定时器,就以这个例子吧。在上面的语句看出,timer->start(100)开始定时器,然后让该线程休眠5ms。结果发现窗口未响应,直接卡死。

    解析:因为之前的例子全是单作业的。Sleep哪怕5ms,cpu也会去处理其他作业。也是因为 Qt 中所有界面都是在 UI 线程中(也被称为主线程,就是执行了QApplication::exec()的线程),在这个线程中执行耗时的操作(比如那个循环),就会阻塞 UI 线程,从而让界面停止响应。

    那么就使用线程来试试,主窗口实现计时器的不断累加,将sleep放在线程中。如:线程中sleep(50),线程休眠50ms,而主窗口的线程的计时器也将要接近50ms。是否就可以说明窗口的线程与子线程是同时的呢?

    Qt中线程的创建和使用:

     

    解析:新建一个c++类(不需要有设计器的),MyThread作为线程类。此时要修改该线程类的基类,(由QWidget改为QThread).使用的线程处理函数是Qthread类中的虚函数(见下图)。

     

          简单说,创建一个线程类,线程中的所有工作全部在该类中编写。接口:emit xxx。发出的信号就是该类的接口。便于其他类或窗口使用。

    线程类:

    class MyThread : public Qthread

    {

    protected:

        void run();

    signals:

        void MySignal();

    }

    重写线程处理函数:

    void MyThread::run()

    {

        QThread::sleep(50);

        emit MySignal();

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

    }

    主窗口类,启动定时器,并开启线程

    void Widget::on_pushButton_clicked()

    {

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

            timer->start(1000);

        myThread.start();

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

    }

    //接受线程发出的信号

     connect(&myThread,&MyThread::MySignal,

                [=]()

                {

                    qDebug()<<"over";

                    //timer->stop();

                }

    如图主线程的ID与子线程的ID不同,子线程休眠时间与主线程运行时间相近:

    子线程关闭问题

    当我们关闭窗口或关闭主线程,但是子线程仍然在运行。如下,我们的计时器已经暂停了,但是子线程还执行。

    解决方法:手动调用Qthread类的槽函数。

    注意:建议使用quit(),该函数会等待子线程执行完全部操作,再关闭。而terminate()调用后立即关闭线程,不管线程的后续操作,直接终止。

    当窗口关闭时,关闭子线程

       connect(this,&QWidget::destroyed,

               [=]()

               {

                    myThread.quit();

                    myThread.wait();

               }

       );

    源代码:

    mythread.h

    #ifndef MYTHREAD_H

    #define MYTHREAD_H

    #include <QWidget>

    #include <QThread>

    class MyThread : public QThread

    {

        Q_OBJECT

    public:

        explicit MyThread(QObject *parent = 0);

    protected:

        void run();              //线程处理函数

    signals:

        void MySignal();      //线程信号

    public slots:

    };

    #endif // MYTHREAD_H

    mythread.cpp

    #include "mythread.h"

    #include <QDebug>

    MyThread::MyThread(QObject *parent) :

        QThread(parent)

    {

    }

    //重写线程处理函数

    void MyThread::run()

    {

        QThread::sleep(50);

        emit MySignal();            //线程完成操作,发出信号

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

    }

    widget.h

    #ifndef WIDGET_H

    #define WIDGET_H

    #include <QWidget>

    #include <QTimer>

    #include "mythread.h"

    namespace Ui {

    class Widget;

    }

    class Widget : public QWidget

    {

        Q_OBJECT

    public:

        explicit Widget(QWidget *parent = 0);

        ~Widget();

    private slots:

        void on_pushButton_clicked();          //开始按钮

        void on_pushButton_2_clicked();       //暂停按钮

    private:

        Ui::Widget *ui;

        QTimer *timer;                   //定时器对象

        MyThread myThread;                //线程对象

    };

    #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);

        timer=new QTimer(this);

    //定时器槽函数

        connect(timer,&QTimer::timeout,

                [=]()

                {

                   static int num=0;

                   ui->lcdNumber->display(num);

                   num++;

                }

    );

    //线程信号槽函数

        connect(&myThread,&MyThread::MySignal,

                [=]()

                {

                    qDebug()<<"over";

                    timer->stop();

                }

       );

    //关闭窗口,并关闭子线程

       connect(this,&QWidget::destroyed,

               [=]()

               {

                    myThread.quit();

                    myThread.wait();

               }

       );

    }

    Widget::~Widget()

    {

        delete ui;

    }

    //开始按钮,开始定时器和线程

    void Widget::on_pushButton_clicked()

    {

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

    {

            timer->start(1000);

    }

        myThread.start();

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

    }

    void Widget::on_pushButton_2_clicked()

    {

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

    {

                  timer->stop();

    }

    }

  • 相关阅读:
    72. Edit Distance
    电脑常识
    java try·····catch·····异常处理学习
    java链接sqlserver数据库
    HTTP Status 500
    初识NDA
    Sublime Text_v2.02包含中文包以及使用方法
    ol 与ul 的区别
    word-break: break-all word-break:keep-all word-wrap: break-word三者的区别
    用deamon打开ISO文件,提示命令行错误!!
  • 原文地址:https://www.cnblogs.com/gd-luojialin/p/9215818.html
Copyright © 2020-2023  润新知