多线程

什么是线程?

  说到线程,我们先来说一下什么是进程,进程是一个运行起来的程序,在操作系统中,一个程序运行起来,程序被加载到内存中,操作系统创建一个进程描述符(进程控制块)pcb对程序的运行进行描述控制,因此进程就是pcb,在Linux中进程是task_struct结构体。
  Linux线程使用进程pcb模拟实现,因此Linux的线程可以说是一个轻量级的进程,如果说pcb是线程,那么进程就是一个线程组,在一个进程中有一个或多个线程。由于CPU调度的是pcb,那么在Linux下,线程是CPU调度的基本单位,由于程序运行的时候分配进程资源,所以说进程(线程组)是资源分配的基本单位。
  在同一个进程中的线程的共享同一块虚拟地址空间,所以线程之间的通信非常简单,那么这么多线程在调用的时候为什么不会出现栈混乱?接下来为大家介绍同一个进程中个线程之间的共享和独有资源。
线程的共享资源:文件描述符、每种信号的处理方式、当前工作目录、用户ID和组ID
线程的独有资源:栈、寄存器、信号屏蔽字、errno、线程标识符

多线程优点:

1、线程之间通信简单
2、线程的创建/销毁成本低
3、线程的调度成本更低
4、线程的执行粒度更细

多线程缺点:

1、线程之间缺乏访问控制,编码难度更高(任何一个线程都可以访问虚拟地址空间中的任意进程,数据争抢更频繁)
2、健壮性较低:线程的某些错误会导致整个进程的退出

多线程的使用场景:

  线程创建的越多效率越高,要根据具体的情况而定,比如说IO密集型程序是大量的磁盘IO操作,只有处理完当前文件才会去读取下一个文件,是串行化运行,如果使用多线程,就可以同时读取多个文件,这样的效率会大大提升,还有CPU密集型程序,也是同样的道理,虽然CPU密集型程序是大量的数据运算程序,但它和IO密集型程序一样,使用多线程会提高效率。
线程控制:线程创建、线程终止、线程等待、线程分离

线程控制

线程创建:
  由于系统没有提供直接创建线程的系统调用接口,但是前辈们已经为我们实现了一套线程控制的接口,封装了线程库供我们使用。所以我们创建出来的线程是用户态线程,在内核中对应有一个轻量级进程来实现程序的调度运行
线程终止:
  在main函数中的return退出的是进程,普通线程入口函数中return只是退出调用线程,pthread_exit()退出的是正在调用的线程,pthread_cancel()退出的是指定线程
线程等待:
  线程等待指的是等待指定的线程退出,并且获取其返回值,回收线程资源,避免产生僵尸线程。一个县城被创建他的默认属性是joinable,处于这个属性的县城都要被等待,因为这样的线程在退出之后不会自动回收资源,会造成资源泄露,要使用pthread_jion获取线程退出的返回值。
线程分离:
  线程分离指的是将线程的joinable属性改为detach属性,处于这个属性的线程在退出后可以自动回收资源,不用等待也不能等待,否则会报错,这个属性使用pthread_detach()来设置,可以将任意线程在任意时刻分离,还可以分离自己。

线程安全:

  线程安全指的是多个执行流同时对临界资源进行操作而不会造成数据二义性。

实现线程安全的方法(同步和互斥):

同步:资源访问的合理性,也就是对临界资源访问的时序可控性,通过等待和唤醒来实现
互斥:资源访问的安全性,也就是对临界资源同一时间的唯一访问性,通过互斥锁来实现

STL中的容器的线程安全

  STL中的容器都是非线程安全的,因为在当初设计的时候就是为了追求高性能,但是用户在使用的时候可以自己来为其添加线程安全。

互斥与同步的实现

互斥的实现:
  通过互斥锁来实现,互斥锁是一个只有0或1的计数器,是一个原子操作,互斥锁需要注意的是在加锁之后要在任何一个有可能退出线程的地方解锁,加锁的步骤是定义->初始化->加解锁->销毁锁
条件变量
  条件变量用于实现线程之间的同步,实现方法是等待和唤醒。由于条件变量本身并不具备操作条件判断,对条件变量的判断是对一个临界资源操作,所以需要配合加解锁来操作

生产者与消费者模型

  生产者和消费者模型其实就是实现一个线程安全的队列,在这个队列中有多个生产者、多个消费者和多个产品,多个生产者向队列中生产产品,多个消费者从队列中消费产品。生产者与消费者之间的关系有三种,分别为:生产者与生产者之间是互斥关系,消费者与消费者之间是互斥关系,生产者与消费者之间是同步+互斥关系。
为什么要使用线程安全队列?
解耦合:生产者不需要直接给消费者提供对应的接口,只需要将数据生产到队列中即可。
支持忙闲不均:如果生产者生产的数据量过大,消费者来不及处理,就可将数据存储到队列中,这样就可以避免数据过多来不及处理而丢失
支持并发处理数据:支持多个生产者或消费者同时并发处理

信号量

  具有等待队列的计数器,主要进行资源计数、唤醒和等待功能。信号量主要用于实现线程或进程之间的同步,它与条件变量的差别是条件变量需要外部条件来执行判断,而信号量是根据自身数据来执行判断的。
  信号量初始一个时间计数,当获取资源时,先开始判断计数,如果计数>0,表示有资源,信号量减一,程序继续运行,如果计数<=0,表示没有资源,程序阻塞等待,当资源产出,计数加一,立即唤醒等待队列上的线(进)程。

读写锁:

  利用两个计数器,来实现写互斥、读共享,也就是说写的时候不能读也不能有第二个写者,读的时候可以有多个读者,但是不能写,简单说就是可以同时有多个读者读,但是同时只能由一个写者写。
  读写锁在加锁时阻塞:使用自旋锁,循环条件判断,条件判断比较及时,但是比较消耗CPU。读写锁通常用于读者多,写者少的情况。当想要加写锁修改数据时,总有线(进)程加读锁,这就会导致饥饿问题,因此读写锁是由优先级的,默认的是读优先。如果设置的是写优先,加写锁加不上在等待期间就拒绝后续的加读锁操作,反之同理。

线程池:

线程池使用线程(有上限)+任务队列(线程安全)
任务队列在线程中的作用:
1、避免大量线程创建/销毁的时间成本
2、避免峰值压力,导致线程创建过多,资源耗尽,程序奔溃
3、解耦和:
4、支持忙先不均
5、支持并发进行任务处理(提高了任务处理的效率)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值