目录
- 一.什么是CPU?
- 二.单核处理器与多核处理器
- 三.什么是进程和线程?
- 3.1 定义
- 3.2 以餐厅为例子解释进程和线程
- 3.2 以QQ音乐为例子,解释QQ音乐里面的进程和线程
- 四.Qt中的多线程
- 五.Qt多线程案例
- 任务描述
- 案例演示
- 设置显示内容的字体大小和位置
- 运行
- 创建一个线程类
- mywork.h
- mywork.cpp
- widget.h
- widget.cpp
- 问题:当使用对按钮右键创建的槽函数执行doWork时,主线程和子线程是同一个。
- 参考链接
一.什么是CPU?
CPU(中央处理器)是计算机的核心组件,它内部包含了多个功能单元和寄存器,用于执行各种指令和完成计算任务。以下是CPU内部的一些主要部分:
-
控制单元(Control Unit): 控制单元负责解释和执行计算机程序中的指令。它从内存中读取指令,对指令进行解码,并控制其他部分执行相应的操作。
-
算术逻辑单元(Arithmetic Logic Unit,ALU): ALU执行各种算术和逻辑运算,包括加法、减法、乘法、除法等数学运算,以及逻辑运算如与、或、非等。
-
寄存器(Registers): 寄存器是用于存储临时数据的高速存储单元。CPU包含多个寄存器,其中包括通用寄存器、数据寄存器、地址寄存器等。寄存器的使用可以提高数据访问速度。
-
时钟和定时器(Clock and Timers): 时钟是CPU的心脏,通过时钟信号来同步各个部件的工作。定时器用于测量时间,处理时钟周期。
-
缓存(Cache): 缓存是一种高速临时存储,用于存放频繁访问的数据和指令,以提高访问速度。CPU通常包含多级缓存,包括一级缓存(L1 Cache)和二级缓存(L2 Cache)等。
-
总线接口(Bus Interface): 总线接口负责与计算机的其他部分进行通信,包括内存、输入输出设备等。它通过数据总线、地址总线和控制总线传输信息。
-
指令寄存器(Instruction Register,IR): 存储当前正在执行的指令,控制单元从中读取指令进行解码和执行。
-
程序计数器(Program Counter,PC): 存储下一条要执行的指令的地址,以保持程序的顺序执行。
这些部分协同工作,使得CPU能够有效地执行各种计算和控制任务。不同的CPU架构和制造商可能会有一些额外或不同的部分,但这些是CPU内部常见的组成部分。
二.单核处理器与多核处理器
一个处理器(CPU)拥有的运算核心的数量,可以区别CPU是单核还是多核。
同一时刻,CPU的每个核心都可以运行1个线程
。单核和多核CPU的区别就是同一时刻可以运行几个线程,单核CPU只能运行一个,N核可以运行N个
。
值得一提的是,单核CPU也可以“同时”运行多个线程。不过这个“同时”只是一种假象,因为本质上单核CPU不能同时运行多线程。它的运行原理便是前文提到的CPU调度,在不同时间片内运行不同线程,由于每个时间片很短所以看起来是多个线程同时进行的,这也是所谓的并发。
三.什么是进程和线程?
3.1 定义
什么是进程?
进程(Process)是操作系统中的一个执行单位,它包含了程序代码、数据以及进程所需的系统资源(如内存、文件等),是一个独立的、运行中的程序实体。每个进程都有自己的独立内存空间,因此多个进程之间互不干扰。在现代操作系统中,每个进程都有一个唯一的进程标识符(PID),用于操作系统进行进程管理和调度。
什么是线程?
线程是操作系统能够进行运算调度的最小单位。一个线程包括了指令、寄存器和栈等信息。一个进程可以包含多个线程,这些线程共享进程的资源,但拥有各自的独立执行流。
3.2 以餐厅为例子解释进程和线程
一个餐厅是一个进程,厨师是一个线程,服务员是另一个线程。让餐厅在炒饭的同时,还可以服务客人。
具体如下:
1. 进程(Process):
想象一家餐馆,餐馆就是一个进程。在这个餐馆里,有独立的厨房、服务员、顾客等。每个进程是一个独立的执行环境,有自己的内存空间、资源、代码等。就像每家餐馆有自己的厨房、餐具和服务。
- 例子: 一家餐馆可以同时为多桌客人提供服务,每桌客人独立享受他们的用餐体验,互不影响。如果一个桌子上的服务出了问题,其他桌子的服务仍然可以继续。
2. 线程(Thread):
现在,我们把餐馆的工作人员看作线程。在一个餐馆中,有多个工作人员,比如厨师、服务员等。每个线程负责执行不同的任务,但它们共享餐馆的资源,比如厨房、库存等。
- 例子: 厨师和服务员是餐馆的两个线程。厨师负责烹饪食物,服务员负责上菜和为客人提供服务。这两个线程协同工作,共同维护餐馆的正常运作。
3. 多线程:
多线程就像在同一个餐馆里有多个工作人员(线程),它们可以并行执行不同的任务,提高工作效率。
- 例子: 当有多个服务员为不同的桌子服务时,如果其中一个服务员忙于为一桌客人点菜,其他服务员仍然可以为其他桌子提供服务,使整个餐馆更高效。
总结:
-
进程: 相当于一个完整的餐馆,拥有独立的资源和环境。
-
线程: 相当于餐馆里的工作人员,共享餐馆的资源,协同完成任务。
-
多线程: 相当于在同一个餐馆里有多个工作人员,可以并行执行任务,提高效率。
3.2 以QQ音乐为例子,解释QQ音乐里面的进程和线程
启动QQ音乐,系统会给QQ音乐创建一个进程,这个进程包含QQ音乐的代码,数据,内存等。
播放音乐是一个线程,界面的刷新也是一个线程。因此刷新界面和播放音乐可以同时进行。
详细内容如下:
-
进程(Process): 在计算机中,进程是程序的一次执行过程
。当你启动QQ音乐软件时,操作系统会为QQ音乐创建一个独立的进程。这个进程包含了QQ音乐的代码、数据、内存等资源。不同的应用程序通常在不同的进程中运行,相互之间不会直接影响
。 -
线程(Thread): 线程是进程内的一个独立执行单元。在QQ音乐的进程中,可能会有多个线程同时执行。每个线程负责处理不同的任务,比如一个线程用于播放音乐,另一个用于界面的更新,等等。线程之间可以共享进程的资源,但它们有自己的执行上下文和状态。
在QQ音乐的运行过程中,进程和线程的关系可以理解为:
-
一个进程(QQ音乐的进程): 包含了QQ音乐软件的全部资源,是一个独立运行的程序实例。
-
多个线程: 在该进程内,可能有多个线程并发执行,每个线程负责不同的任务,比如负责播放音乐、更新用户界面等。
这样的设计有助于提高程序的并发性和响应性。不同的线程可以同时执行,使得在播放音乐的同时,界面仍然可以保持响应。然而,需要注意线程之间的协同和同步,以避免竞争条件和其他并发问题。
四.Qt中的多线程
默认的线程在Qt中是窗口线程,也叫主线程,负责窗口事件处理或窗口控件数据的更新。
子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理。
主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制。
五.Qt多线程案例
任务描述
用一个窗口显示从0-1千万。
问题:程序在从0数到1千万时,窗口无响应,无法移动窗口。等数到1千万时窗口才恢复正常。这是由于主线程在数数时无法刷新窗口造成的。
解决方法:需要主线程显示数字,子线程进行数数。
案例演示
设置显示内容的字体大小和位置
运行
发现程序在进行0到1千万的运算时,无法拖动窗口界面,甚至窗口卡顿。
这是因为只有一个线程的原因,该线程进行从0到一千万的运行,无法刷新窗口界面。
创建一个线程类
mywork.h
#ifndef MYWORK_H
#define MYWORK_H#include <QObject>class MyWork : public QObject
{Q_OBJECT //Q_OBJECT 宏用于标记一个类,使其能够使用Qt的元对象系统,如信号与槽、动态属性等。
public:explicit MyWork(QObject *parent = nullptr);void doWork();signals:void curNumber(int num); //自定义信号,传递数据
};#endif // MYWORK_H
mywork.cpp
#include "mywork.h"
#include <QThread>
#include <QDebug>MyWork::MyWork(QObject *parent) : QObject(parent)
{}void MyWork::doWork()
{qDebug()<<"当前线程对象的地址:"<<QThread::currentThread();int num=0;while (1){emit curNumber(num++); // 发送信号,信号内容是num++if(num == 10000000){break;}QThread::usleep(1); // 这个函数的作用是让当前线程休眠指定的微秒数,让数字数的不那么快。使用线程休眠时需要注意不要在主线程(UI 线程)中调用,以免造成界面卡顿。}qDebug()<<"run()执行完毕,子线程退出...";
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "mywork.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:
// void on_pushButton_clicked();void showNum(int num); //用来展示数字private:Ui::Widget *ui;MyWork* worker;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QThread>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);// 设置标签中的文本对齐方式ui->label->setAlignment(Qt::AlignCenter);qDebug()<<"主线程对象地址:"<<QThread::currentThread();//创建线程对象QThread *sub = new QThread;worker = new MyWork;//将创建的工作类对象移动到创建的子线程对象中worker->moveToThread(sub);//启动线程sub->start();//将点击按钮事件与doWork函数绑定。如果用按钮自带的槽函数,不知道怎么回事,子线程和主线程是同一个connect(ui->pushButton,&QPushButton::clicked,worker,&MyWork::doWork);//将用于传递数字的信号curNumber和用于在窗口标签中展示的槽函数showNum绑定connect(worker,&MyWork::curNumber,this,&Widget::showNum);
}Widget::~Widget()
{delete ui;
}// 槽函数,用于接收信号传递的数字,然后在标签中显示数字
void Widget::showNum(int num)
{ui->label->setNum(num);
}//点击鼠标,子线程中的doWork函数开始工作
//void Widget::on_pushButton_clicked()
//{
// worker->doWork(); //doWork中每数一个数会通过信号curNumber发送给接收者
//}
问题:当使用对按钮右键创建的槽函数执行doWork时,主线程和子线程是同一个。
数数时,界面依旧卡顿。不知道为什么。
参考链接
Qt中多线程-线程池的使用-C/C++/qt