自学内容网 自学内容网

【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)!