一.什么是线程
在博主之前的进程相关博客中,特别是关于进程的讨论部分,似乎总是将进程视为系统最基本的执行单元。
也就是说,所有代码或任务默认都是由进程来处理的。
这种说法既对也不对。
确实,任务最终都是由进程处理的,但实际上并非进程直接处理,而是通过进程分配的一个或多个线程来完成。
1.定义
这里我们就要引入概念了
进程是系统的资源分配的基本单位
线程是系统的执行的基本单位
这样可能比较难理解,这里就举个例子:
比如说
在一个班级中设有多个小组,老师为每个小组分配不同的任务:
1.每个小组代表一个进程,各小组之间相互独立运行,互不干扰,且有各自任务,资源不互通。
2.老师相当于操作系统,负责任务分配与资源调度。同时老师为每个小组审批资金、资料等必要资源,且给他们各自分配任务。
3.
i:每个小组至少有一名成员,负责利用小组(进程)的资源完成任务,这名成员即为线程。
ii.当小组有多名成员时:
- 他们共享小组(进程)的资源,可相互协作使用
- 各自承担小组(进程)分配的子任务,彼此独立执行
- 最终目标一致,即完成整个小组(进程)的任务。
所以说:
进程是资源容器——系统分配资源,分配任务,让进程来完成
线程是系统的最小执行流——进程让线程去执行,且给他们共享资源
2.Linux中的线程实现
为什么要单拎出来,因为Linux线程实现确实有些不一样
从前面看出,一个进程能有至少一个线程
所以进程和线程的比例是1:n
所以线程多了操作系统也要进行管理
就要经过先组织,再描述
你试着想想:
一个进程有很多个线程,然后同时操作系统还要管理很多个进程,你每个线程还要有独立的结构,还有优先级,然后多个线程还隶属一个PCB
那操作系统设计起来不是很累吗,相当于又多出了一个进程体量的结构体
很巧的是windows,就是这么实现的,线程整一个全新的结构出来,优先级,调度方式,全都重新设计一遍
【用的是tcb结构体,thread ctrl block】
但是Linux就不一样了,它用的就是task_struct结构体,相当于复用了
这样同样有好处:维护成本低,在服务器这种需要长时间运行的机器中优势明显,不会有明显问题,像windows,你让他跑个几年服务器,还是做不到的
同时说明了:
Linux中没有真正意义上的线程,而是用进程模拟的线程
直接复用进程数据结构和管理算法,用进程的struct task_struct来模拟线程
所以说,在Linux中的线程,叫做轻量级进程
【内核没有很明显的内核概念】
那这样做有没有违反操作系统的标准呢?
很显然:
在操作系统规定了计算机系统必须要有线程
linux你可以说它没有真正的线程,但是系统运行的时候是由线程这个东西的,所以说linux只是实现方式不一样
所以两者的区别,是实现方式上的区别,并不意味着Linux不是操作系统了
3.线程和进程的关系
一个进程内必须要有一个线程来执行任务
之前前面的博客我们都是用的一个进程只有一个线程
这个线程也就是主线程
就算是多线程也是一个进程搭配上一个线程
线程在Linux中称为轻量级进程,所以说从名字上来看,线程的执行粒度比进程细了很多,而且更加轻量化
1.在Linux中,线程在进程内部执行,线程在进程的地址空间中运行
2.Linux中,线程的执行粒度比进程更细——线程是执行进程代码的一部分
(任何执行流要执行, 都有资源,地址空间是进程的资源窗口 )
i.线程的私有数据
线程基本上共享进程的资源
线程也有自己私有的数据
线程ID
一组寄存器
栈
errno
信号屏蔽字
调度优先级
最重要的两个字段:
1.线程的上下文【一组寄存器】
2.线程的栈结构
不会出现多线程执行错乱
这些会放在之后的博客上来说
ii.进程和线程共享的数据
文件描述符表
每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
当前工作目录
用户id和组id
共享的最重要的部分:文件描述符表
这些也会在之后的博客里面说
4.线程的优缺点
i.优点
1.创建一个新线程的代价要比创建一个新进程小得多
因为创建一个线程,不需要像创建进程一样,要创建页表和继承地址空间
2.与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
线程在执行,本质就是进程在执行,因为线程是进程的执行分支
所以每个线程都是为一个进程办事,资源都是基本共享的
而在CPU中有个CACHE缓存, 用来缓存高频被调用的数据(热数据)
每次进程调换的时候需要把CACHE里面的热数据全部重新调换
而线程切换的时候不需要调换
3.线程占用的资源要比进程少很多
毕竟更轻量化
4.能充分利用多处理器的可并行数量
5.在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
6.计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
7.I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作
i.缺点
1.性能损失
这个主要是针对线程太多,而CPU的并发器少的情况
2.健壮性降低
这个就很有说法了
当一个进程中的一个线程出问题时,整个进程都要被毙掉,所以要求就很高
3.缺乏访问控制
4.编程难度提高
二.操作演示使用
这里就随便演示一个代码把,体验一下线程,具体操作函数和接口放之后的博客上说、
#include<iostream>
#include<pthread.h>
#include<unistd.h>
void* pthread_run(void* args)
{
while(1)
{
std::cout<<"run other pthread"<<std::endl;
sleep(1);
}
return nullptr;
}
int main()
{
pthread_t tid;
pthread_create(&tid,nullptr,pthread_run,nullptr);
while(1)
{
std::cout<<"run main pthread"<<std::endl;
sleep(1);
}
}
这里我们能发现,这个主线程和副线程是一起运行的