【Linux从青铜到王者】多线程1
什么是线程
课本:线程是比进程更轻量化的一种执行流 线程是进程内部的一个执行流
我们:线程是cpu调度的基本单位,进程是承载系统分配资源的基本实体
如何看待之前的进程?只有一个执行流的进程
如何看待现在的进程?有多个执行流的线程
一个进程至少有一个主线程,执行main函数的即为主线程,其他线程都称为工作线程
线程划分虚拟地址空间,虚拟地址空间本质也是一种资源——在虚拟地址空间中,有些资源是线程私有的,大部分是线程之间共享的
在Linux中线程被称为轻量级进程(LWP),实际上根本没有线程这个概念
所以上面第一张图的pcb就是一个线程,第二张图的全部才是一个进程
pid和tgid
pid:轻量级进程id,也就是线程id
tgid:轻量级线程组id,也就是进程id
一个进程不管有多少个线程,每个线程的tgid都是一样的,每个线程的pid都是不同的
主线程的pid==tgid
线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多
- 线程占用的资源要比进程少很多 能充分利用多处理器的可并行数量
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务
- 计算密集型应用,为了能在多处理器系统上运行,将计算分解到多个线程中实现
- I/O密集型应用,为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
线程切换为什么更效率?
- 相比于进程切换,线程共享虚拟地址空间的大部分内容,所以线程切换的时候有些寄存器是不用更换的
- 线程切换不用更新cache热数据(局部性原理,cpu要预加载很多东西)
线程的缺点
- 性能损失
- 健壮性降低
- 编程难度增高
- 缺乏访问控制
进程比线程安全的原因是每个进程有独立的虚拟地址空间,有自己独有的数据,具有独立性
线程私有的资源:栈和进程上下文(动态加载的概念),调度优先级,线程标识符...
执行main函数的为主线程,主线程用的栈是虚拟地址空间里的栈,其他线程的栈都在线程库里被开辟并管理
因为线程和进程共享同一地址空间,定义的函数也可以被多个线程看到,全局变量可以被多个线程访问——会导致数据不一致的问题,下文会详解!
因为Linux里是没有线程的,但是我们学的都是线程的概念,为了方便我们使用就产生了pthread库
线程库是共享库,管理一个用户的所有线程
pthread库里所谓的线程其实是对系统里的lwp进行了封装,将线程封装成由三个部分(struct_phthread(lwp),线程局部存储(上下文资源),线程栈)组成的结构体
每个封装好的线程的首地址就是pthread_t,也被称为线程标识符
如何管理线程呢?先描述,再组织!
将一个封装好的线程视为数组的一个元素,使对线程的管理变成了对数组的增删查改
一个用户的每个进程中,每产生一个线程,本质就是在pthread库里管理线程的数组增加一个元素
pthread库又是共享库,会被加载到使用该库的每个进程的地址空间的共享区
如果多个进程都创建了线程,会不会调用到其他进程里的线程呢?个人感觉是不会的,因为有pid和tgid的存在,应该可以通过这个来判断
线程控制
通过上面我们已经知道pthread_t的本质是什么了,也了解pthread库的由来了,接下来就来了解一下线程控制的相关接口吧
一、pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
示例
void* ThreadRoutine(void *args)//注意返回值为void*
{}
int main()
{
pthread_t tid;
pthread_creat(&tid,nullptr,ThreadRoutine,要传给前面函数的参数)
return 0;
}
线程创建成功返回0,失败则返回错误码
void pthread_exit(void *retval);
pthread_t pthread_self(void);
int pthread_cancel(pthread_t thread);
int pthread_join(pthread_t thread, void **retval);
int pthread_detach(pthread_t thread);
线程的等待和分离
线程默认要被等待!
// // 1. 线程退出,没有等待,会导致类似进程的僵尸问题,造成内存泄漏
// // 2. 线程退出时,主线程如何获取新线程的返回值!pthread_join()
互斥锁
锁的原理
死锁
条件变量
pthread_cond_siganl(&_p_cond,&mutex);
该函数等待的时候会自动把锁释放,该条件变量被唤醒的时候又会自动去申请锁资源
一个进程的各个线程之间共享地址空间,也就是说虚拟地址空间的大部分资源都是可以被共享的——全局变量,static常量等
例如全局变量这种多个线程可以一起访问的资源,就称为共享资源
共享资源如果没有被保护的话,可能会出现数据不一致的问题
被保护起来的资源(锁)称为临界资源,访问临界资源的代码块称为临界区
互斥条件:一个资源只能被一个执行流访问
只有互斥会导致饥饿问题
申请锁取决于线程的竞争能力,如果一个竞争能力特别强的线程刚释放锁,又申请到锁继续访问资源,如此循环就会导致其他线程无法申请到锁,导致饥饿问题
当多个线程申请同一把锁的时候,如果线程已经在一个人手里,那么其他线程就会锁处阻塞
同步:在保证资源安全的条件下,让多线程按照一定的顺序执行
怎么实现同步的呢——条件变量
1.不做无效的锁申请 2.让线程按照一定的顺序执行
条件变量相当于一个闹钟,当锁被释放的时候会告诉排队的进程
所以cond里可能封装了flag(表状态)和一个队列
cp问题——生产者消费者模型
321原则
3种关系:生产者和生产者之间——竞争&互斥
消费者和消费者之间——竞争&互斥
生产和消费者之间——互斥&同步
2个角色:生产者(1个或多个进程或线程)消费者(1个或多个进程或线程)
1个空间:在内存空间进行交换
效果:可以让多执行流解耦,提高处理数据的能力
原文地址:https://blog.csdn.net/fight_for1/article/details/136702652
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!