28初识线程

Posted gd-luojialin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();

}

}

 

以上是关于28初识线程的主要内容,如果未能解决你的问题,请参考以下文章

多线程初识

初识多线程__下

Java核心知识---初识线程

多线程初识多线程

初识多线程

初识线程