线程安全(互斥 死锁 同步)

线程安全

线程安全的实现:同步 互斥

互斥:同一时间只能有一个线程能够访问资源实现资源访问的安全性

同步:通过条件判断实现线程对临界资源访问的合理有序

互斥:

互斥锁(通过互斥锁保护线程对临界资源的访问操作不会被打断)

原理:标记临界资源的两种状态(可访问/不可访问)在线程访问临界资源之前,先进行互斥锁加锁操作   在线程访问临界资源完毕后,进行解锁操作

互斥锁本身计数器的操作是原子操作

  • 定义互斥锁变量:pthread_mutex_t   mutex;
  • 初始化互斥锁:pthread_mutex_t   mutex=PHREAD_MUTEX_INITIALIZER

                             int  pthread_mutex_init(pthread_mutex_t * mutex,pthread_mutexattr_t * attr);

  • 在访问临界资源之前进行加锁

          int  pthread_mutex_lock(pthread_mutex_t * mutex);   阻塞

         int  pthread_mutex_trylock(pthread_mutex_t * mutex);   非阻塞

  • 在访问临界资源完毕后解锁

          int  pthread_mutex_unlock(pthread_mutex_t * mutex); 

  • 不再使用互斥锁,则销毁

           int  pthread_mutex_destory(pthread_mutex_t * mutex); 

应用:黄牛抢票,创建四个黄牛线程  共抢100张票

加入互斥锁后发现,虽然抢票安全,不会出现越界,但存在一定的不合理性,互斥是无法保证对资源访问的合理有序性

分析主要原因是,黄牛抢票入口函数中sleep函数导致在获取到锁后需要运行一定时间,此时尚未解锁,即使时间片轮转到其余三个线程 但无法获取锁处于阻塞状态,当时间片再次轮转到该线程,该线程执行完毕解锁,但由于while循环,在时间片还没结束的情况下再次获取到锁,导致始终一个线程获取到锁。

死锁

程序因流程无法继续推进卡死的情况

产生死锁的四个条件:

  1. 互斥条件,同一时间只有一个线程能够访问资源
  2. 不可剥夺条件  我加的锁只有我能解
  3. 请求与保持条件  得到A锁然后请求B锁,请求不到B,也不释放A
  4. 环路等待条件:A拿到1请求2 锁    B拿到2请求1锁

预防死锁:(破坏死锁产生条件)

1,保证加/解锁顺序一致

2,使用非阻塞加锁

避免死锁:银行家算法

同步  

条件变量:一个pcb等待队列+使线程阻塞以及唤醒接口

原理:当线程获取资源不合理的时候,调用阻塞接口,使线程阻塞,将pcb挂到等待队列,等待资源访问合理的时候通过唤醒接口唤醒阻塞的线程

注意:条件变量自身只提供阻塞和唤醒接口,获取资源是否合理需要程序员自己判断

  • 定义条件变量:pthread_cond_t   cond;
  • 条件变量初始化:

pthread_cond_t   cond=PHREAD_COND_INITIALIZER

 int  pthread_cond_init(pthread_cond_t * cond,pthread_condattr_t * attr);

  • 在获取资源不合理时调用阻塞接口使线程阻塞

int  pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex);只要调用就阻塞  没有唤醒则一直等待

int  pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,struct timespec* abstime);等待一定时间后自动唤醒

  • 其他线程促使资源获取合理后,唤醒等待队列上的线程

int pthread_cond_signal(pthread_cond_t * cond);唤醒一个

int pthread_cond_broadcast(pthread_cond_t * cond)  唤醒所有

  • 销毁

int  pthread_cond_destory(pthread_cond_t * mutex); 

应用:厨师-顾客创建两个线程,以柜台上饭的数量作为临界资源

厨师接口:

  • 加锁,防止在访问条件变量时其他线程同时会访问
  • 条件变量进行判断,根据临界资源,是否有饭作为判断条件,对于厨师来说,有饭则不需要做饭则阻塞,没饭则做饭
  • 做饭使得临界资源数量+1,可以唤醒顾客吃饭
  • 解锁

顾客接口:

  • 加锁,防止在访问条件变量时其他线程同时会访问
  • 条件变量进行判断,根据临界资源,是否有饭作为判断条件,对于顾客来说,没有饭则阻塞,有饭则吃饭
  • 吃饭使得临界资源数量+1,可以唤醒厨师做饭
  • 解锁

注意:1条件判断应该使用while循环

原因分析:如果存在多个顾客情况,开始没有饭,第一个顾客发现没有饭阻塞解锁,其余顾客拿到锁进入也因为没有饭而阻塞,导致多个顾客均阻塞,当时间片轮转,厨师拿到锁做了一碗饭,开始唤醒顾客,多个顾客都被唤醒,但只有其中一个顾客拿到了锁,其他顾客因抢不到锁而阻塞,等待这个顾客吃完饭解锁后,可能会因为时间片轮转,拿到所得不是厨师而是其余顾客,如果没有二次条件判断就会出现在没有临界资源(没有饭)情况下吃到饭。

           2在具有多个角色的情况下,应该使用多个条件变量

原因分析:多个顾客,因为没有饭吃,被挂载在阻塞队列上,一个厨师做好饭,唤醒一个顾客,顾客吃完饭唤醒阻塞队列上的线程,但因为顾客在前,厨师在后,有可能再次唤醒的不是厨师而是顾客,该顾客会因为二次条件判断没有饭再次阻塞,对此需要根据各自创建不同的条件变量,从而有不同的阻塞队列。

           3条件判断会对临界资源进行访问 ,需要搭配互斥锁使用

生产者与消费者模型

应用于大量数据产生及处理的场景

优点:解耦合(各自完成各自功能)   支持忙闲不均 (满足同步互斥)    支持并发(缓冲区必须实现线程安全)

  • 生产者之间保持互斥关系   
  • 消费者之间保持互斥关系
  • 生产者与消费者之间保持同步与互斥关系

生产者消费者模型,在大量数据需要处理的情况下所产生的一种应用场景,需要借助一个缓冲区来实现,这个缓冲区就是阻塞队列BlockQueue

  • 创建两种角色的线程,各自负责数据的产生与消费功能
  • 线程安全的任务数据队列 ,向外提供入队与出队操作

线程安全的任务数据队列通过class BlockQueue  阻塞队列实现:

  • 有空闲节点可以入队数据,没有空闲节点则阻塞入队线程
  • 有数据节点可以出队数据,没有数据节点则阻塞出队线程
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HT . WANG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值