线程安全

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

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

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

互斥:

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

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

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

  • 定义互斥锁变量: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. BCB线程的互斥与同步

    线程的互斥与同步     互斥控制是为了避免一个线程在使用某一个对象或全局变量与其他线程发生冲突.实现线程互斥的方法有: (1)   访问代码委托给VCL主线程执行.在线程中若要调用可视化的方法或访问 ...

  2. 线程的互斥与同步机制

    同个人博客:http://tsundere-x.top/ 一.互斥 为何需要引入互斥机制? 当多个线程对同一数据并发读写(至少有一个线程执行写操作)时,这种情形被称为竞争.竞争会导致数据读或写的不确定 ...

  3. 南京邮电大学操作系统实验二:线程的互斥与同步

    实验原理及内容 基于互斥锁的临界区管理 使用编辑器gedit 2_1.c,新建一个2_1.c源文件,创建双线程并发完成订票操作,输入后面的范例代码: #include <stdio.h> ...

  4. 进程、线程知识点总结和同步(消费者生产者,读者写者三类问题)、互斥、异步、并发、并行、死锁、活锁的总结

    转自:http://www.cnblogs.com/kubixuesheng/p/4355786.html 进程:是个动态的概念,指的是一个静态的程序对某个数据集的一次运行活动,而程序是静态的概念,是 ...

  5. 线程互斥和同步-- 互斥锁

    一. 线程分离 我们一般创建的线程是可结合的,这个时候如果我们调用pthread_jion()去等待的话,这种等待的方式是阻塞式等待,如果主线程一直等待,主线程就无法做其他的事情了,所以应该使用线程分 ...

  6. python多线程编程(2): 使用互斥锁同步线程

    上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的代码: # ...

  7. python 线程锁_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  8. python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

  9. python多线程同步与互斥_python多线程编程(3): 使用互斥锁同步线程

    问题的提出 上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的 ...

最新文章

  1. mysql 创建date,在MySQL中从DATE和TIME创建DATETIME?
  2. 十一、explain属性介绍
  3. oracle数据库用户密码将要过期处理办法(ORA-28002)
  4. 蒙特卡洛粒子滤波定位算法_ROS -- 最简单的自主ACML定位
  5. 你写的代码一点都不 Pythonic
  6. mysql连接池_数据库技术:数据库连接池,Commons DbUtils,批处理,元数据
  7. tps 交易量_交易处理系统(TPS)
  8. 为何亏损还要烧更多的钱?这次何小鹏说清楚了
  9. 红帽学习笔记[RHCSA] 第五课[用户、权限相关]
  10. SAP MM 用客户寄售库存管理方式来管理周转箱等可回收物料
  11. 数据库多表查询的几种方法
  12. 什么叫运营---一个人,一张网,一艘船,独钓寒江雪!
  13. 机器学习算法(一) K-Means算法简述以及在MNIST上的聚类实现
  14. ps基础入门3-文字样式
  15. python实现归结演绎推理_归结演绎推理.ppt
  16. Android----搜索历史(带区分切换用户id)
  17. 【pano2vr】网页Flash中简单实现炫酷的3D模型制作
  18. Python小白学习第二天
  19. Keil升级到AC6后,到底有哪些变化?
  20. html打造动画【系列2】- 可爱的蛙蛙表情

热门文章

  1. python入门—如何运行python程序
  2. 电子血压计并非人人适用
  3. 一些学习Android开发的网站分享
  4. 企业微信的后台怎么进入和管理?
  5. [Deconv]|[cnn]Deep visualization toolbox安装
  6. 当当网年中图书大促来了,全年买书最好时机
  7. 2021 ICPC Asia Regionals Online Contest (II) Problem G. Limit
  8. Linux(Centos6、Centos7)常用关机命令(halt命令需针对不同版本)
  9. frameset标签
  10. P4:编程网络的转发平面