1. 概述

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

2. 优势

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

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

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

3. 特点

(1) 多线程程序的行为无法预期,当多次执行上述程序时,每次的运行结果都可能不同

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

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

(4) 由于多线程对代码的敏感度高,因此对代码的细微修改都可能产生意想不到的结果

4. 简单实例

    单击"start"按钮将启动数个工作线程(工作线程数目由MAXSIZE宏决定),各个线程循环打印数字0~9,直到单击"stop"按钮终止所有线程为止

(1) threaddlg.h

#ifndef THREADDLG_H
#define THREADDLG_H

#include <QDialog>
#include <QPushButton>
#include <workthread.h>

#define MAXSIZE 1

class ThreadDlg : public QDialog
{
    Q_OBJECT
public:
    //explicit关键字禁止构造函数隐式类型转换(C++ 11新特性)
    explicit ThreadDlg(QWidget *parent = nullptr);

signals:

public slots:
    void slotStart();  //槽函数用于启动线程
    void slotStop();  //槽函数用于终止线程

private:
    QPushButton* m_startButton;
    QPushButton* m_stopButton;
    QPushButton* m_quitButton;

    WorkThread* workThread[MAXSIZE];  //记录了所启动的全部线程
};

#endif // THREADDLG_H

(2) threaddlg.cpp

#include "threaddlg.h"
#include <QHBoxLayout>

ThreadDlg::ThreadDlg(QWidget *parent) : QDialog(parent)
{
    m_startButton = new QPushButton(tr("start"));
    m_stopButton = new QPushButton(tr("stop"));
    m_quitButton = new QPushButton(tr("quit"));

    QHBoxLayout* mainLayout = new QHBoxLayout(this);
    mainLayout->addWidget(m_startButton);
    mainLayout->addWidget(m_stopButton);
    mainLayout->addWidget(m_quitButton);

    connect(m_startButton, SIGNAL(clicked()), this, SLOT(slotStart()));
    connect(m_stopButton, SIGNAL(clicked()), this, SLOT(slotStop()));
    connect(m_quitButton, SIGNAL(clicked()), this, SLOT(close()));
}

void ThreadDlg::slotStart()
{
    //使用两个for循环,目的是使新建的线程尽可能同时开始执行
    for(int i = 0; i < MAXSIZE; ++i)
    {
        workThread[i] = new WorkThread();
    }

    for(int i = 0; i < MAXSIZE; ++i)
    {
        //调用QThread基类的start(),此函数将启动WorkThread类的run(),从而使线程开始真正运行
        workThread[i]->start();
    }

    m_startButton->setEnabled(false);
    m_stopButton->setEnabled(true);
}

void ThreadDlg::slotStop()
{
    for(int i = 0; i < MAXSIZE; ++i)
    {
        //调用QThread基类的terminate(),依次终止保存在workThread[]数组中的WorkThread类实例
        workThread[i]->terminate();
        //terminate()并不会立刻终止这个线程,该线程何时终止取决于操作系统的调度策略,因此,
        //QThread基类的wait()使线程阻塞等待直到退出或超时
        workThread[i]->wait();
    }

    m_startButton->setEnabled(true);
    m_stopButton->setEnabled(false);
}

(3) workthread.h

#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QThread>

class WorkThread : public QThread
{
    Q_OBJECT
public:
    WorkThread();
protected:
    void run();
};

#endif // WORKTHREAD_H

(4) workthread.cpp

#include "workthread.h"
#include <QDebug>

WorkThread::WorkThread()
{

}

void WorkThread::run()
{
    while(true)
    {
        for (int i = 0; i < 10; ++i)
        {
            //此处不使用printf()的原因:线程将因为调用printf()而持有一个控制I/O的锁(lock),多个线程同时调用printf()在某些
            //情况下将造成控制台输出阻塞,而使用qDebug()作为控制台输出则不会出现上述问题
            qDebug() << i << i << i << i << i << i << i << i;
        }
    }
}

(5) 运行结果,左边的图是启动5个线程的运行结果,右边的图是启动单一线程的运行结果。可以看出,单一线程的输出是顺序打印的,而多线程的输出结果是乱序打印的,这正是多线程的一大特点

posted on 2022-03-22 11:05  不再低调  阅读(842)  评论(0编辑  收藏  举报