操作1(不加线程)


操作2(引入多线程)



打印的结果不是我们想要的!
这里造成结果混乱的原因是什么?
原因是: 并发执行,多线程抢占资源。
这是一种异步执行方案。当我们程序链接编译通过之后,我们从主函数开始执行,我们将分配相应的内存区域(用户空间区):代码区,数据区,堆区,栈区。主函数运行,主线程产生,我们在这里定义一个数组,定义5个对象,但是这些对象没有被初始化,不是我们真正意义上的线程对象,是不可被执行。我们在for循环中,依次初始化线程对象,分配相对应的栈帧空间和线程信息。建立这5个线程之后,这5个线程的执行顺序我们不能控制。我们不知道谁会先执行。5个线程是共享同一个屏幕。先调动哪个线程,哪个线程先打印到屏幕。也有可能是第一个线程调动执行,有可能在输出字符A的时候时间片到了,从执行变为就绪,或者因为其他什么原因,变为阻塞了。其他的线程调动执行先打印到屏幕上了。
所以打印的是乱序!

操作3(对多线程进行添加互斥量)
我们希望这5个线程在打印时,不进行相互侵占。

这样执行,会不会出现打印

这种情况?
答案是不会的!因为锁只有一把!!!共享的是同一把锁,如果一个线程获得锁,全部打印之后才结束释放锁,其他线程在此期间获取不到锁,阻塞着。
运行程序!

仍然是异步方向,只是我们加锁了。同步方式是要有次序感!!!
操作4(改变加锁解锁的位置)

这种就会出现打印下面这种情况(这种可能性是有的)(其他线程可能会抢占到这把锁)


操作5(改变加锁解锁的位置)

有可能出现

体现加锁的粒度!

锁的粒度越小,并发性越强,耗的资源越多!(都要加锁,都要消耗资源)

我们在加锁,可能老忘记释放锁,我们用锁的管理来处理(相当于智能指针的概念),唯一性锁,当我们进入函数,要创建对象,调动构造函数,构造函数里面我们调动g_mtx.lock(),加锁,函数结束,从函数退出,局部对象的生存期到了,调动析构函数,析构函数里面有解锁操作。

如果加的是普通锁,第一个线程调动funa,加锁。第二个线程调动funa,就阻塞住。递归锁:只要我这个函数获得锁,不管调动多少次,都可以获得锁,执行下去。

条件变量

条件变量是进行线程同步的有利工具!

生产者和消费者例子(条件变量和互斥量)

我们先来看看
我们让生产者生产10次,消费者消费10次。
我们要让线程同步执行。
无论生产者在前还是消费者在前,让生产者先生产数据,消费者再打印数据,打印完告诉生产者已经取得一个数据,生产者再接着再生产数据。我们要求生产者和消费者有一定的次序。
单纯靠互斥量,我们完成不了这个事情。
我们所谓的互斥量,只能进行互斥,只能互斥生产者生产的时候消费者不能取,消费者取的时候生产者不能生产。不能控制先生产再消费。
我们必须要引入条件变量!


我们分析一下这段代码。
cv.wait();的责任:1.把当前线程挂起2.释放互斥量mtx的拥有权(解锁)
我们有3个状态:就绪态,执行态,阻塞态。
我们拿p当做生产者线程,s当做消费者线程。

如果说我们的生产者线程p被调用,p从就绪态跑到执行态。p运行的时候,进入函数,获得锁,如果这个锁没有被其他线程获得,p就获得锁,进入到for循环,条件变量获得这个锁,等待函数把这个生产者线程挂起到阻塞队列中,是由条件变量标识的阻塞队列,并且释放互斥锁的拥有权。

锁释放掉了。我们的s消费者线程,开始执行程序,由就绪态到执行态,这个锁已经被释放,s线程获得锁,进入for循环,处于等待,把消费者s线程挂起到阻塞态,释放锁的拥有权。现在p和s线程都挂起,锁释放掉了。我们得看能否收到一个唤醒(条件变量的消息通知),我们把线程唤起,如果没有消息通知,这两个线程一直在阻塞态,形成死锁。但是形成死锁很困难,因为有虚假唤醒。条件变量上可能会有其他的人发起唤醒。
我们运行程序

处于互相地等待中。处于死锁的过程。
我们增加代码,更易观察。

运行程序

我们看见生产者线程在运行,消费者线程在运行。
我们采取虚假唤醒一下。


出现这个情况的原因是我们得让主线程先睡眠一下。


如果想达到线程的同步,要互斥量和条件变量,还有全局变量!


我们现在有3种状态:就绪,执行,阻塞。
我们创建了2个线程:生产者线程,消费者线程。

我们程序走的时候,比如说,我们消费者先运行,消费者从就绪到达执行状态,打印消费者线程运行的字符串,然后获得锁,向下走,进入到for循环,if为真,进入if内部,执行wait,把当前线程从执行态放入到阻塞态,把线程挂起在条件变量的阻塞队列上,第二步把互斥锁释放掉。我们的消费者阻塞了,生产者开始运行了,从就绪态到达执行态,生产者线程向下执行,这个锁被消费者释放了,生产者获得锁,if的变量为假,不执行if,进行生产,并且把值改为真。并且打印生产者生产了的字符串,然后进行唤醒,把阻塞队列的消费者唤醒了,这种可能性有很多:我们的消费者到达了就绪态,但是有可能我们的生产者从运行跳到阻塞,有可能我们的生产者继续运行。
如果我们的第一种情况,我生产者再进入循环,if的变量为真,进入if,进行了wait,阻塞当前线程(生产者),释放锁。现在我们的消费者已经唤醒了,从等待函数返回,它再一次获取互斥量!!!获得锁,如果互斥量的拥有权仍然是别人拥有,会产生异常。我们打印消费了一个值,由真变为假,又进行了一次唤醒,把生产者唤醒。我们的消费者再执行循环,为假了,求反为真,进入if,执行wait,把消费者变为阻塞态,阻塞在条件变量,并且释放了锁。我们的生产者到达了就绪态,获取锁,继续执行下去!

如果第一次执行,创建线程,p生产者线程和s消费者线程都就绪了,我们生产者从就绪态到达运行态,执行程序,为假,不执行wait,进行生产,把全局变量变为真,接着进行唤醒,实际上没有人阻塞,回过来进行生产,全局变量为真,进入if,把自己阻塞,释放互斥锁,消费者开始执行,获取锁,进入到循环,if的变量为假,不进入if,进行消费,把全局变量变为假,唤醒在条件变量上阻塞的生产者p线程,p到达就绪态。我们的消费者回到循环,if的变量为真,进入if,把自己阻塞到条件变量的阻塞队列,释放锁,生产者p被唤醒,从阻塞到就绪态,获取锁,执行,进行生产,把全局变量变为真,唤醒阻塞的s消费者线程,再回到循环,为真,进入if,执行wait,把自己(生产者)阻塞,释放锁。s消费者进入运行态,获取锁。以此类推。保持了原子性。

我们运行程序!

这就是一种同步!!!
但是如果我们不把主函数的唤醒代码屏蔽掉的话。

程序处于不确定状态。
有可能打印乱序。
虚假唤醒:当我们先执行生产者线程,if为真,把生产者线程阻塞,释放锁,应该被消费者唤醒。消费者阻塞应该被生产者唤醒。但是有可能被其他线程如主线程唤醒。

如何解决虚假唤醒???


把生产者和消费者的if改为while就可以解决虚假唤醒了!!!
如果这个条件为假,当我们被唤醒,也为假,只有生产完才为真,如果中途被唤醒,则就是虚假的唤醒!

第二个例子(给队列,进行生产和消费)




最多生产5个,满了不能生产。




运行正确!

惊群现象

假如我们的很多线程都处于等待状态,你执行notify_all,唤醒所有线程,但是只有一个线程获得资源,其他线程没有获得资源。

219-C++多线程(条件变量)相关推荐

  1. 5.3多线程条件变量

    多线程条件变量应用例子 Input_manager.h InputOpr 结构体添加 进程 ID :pthread_t t_TreadID; int AllInputDevicesInit(void) ...

  2. linux多进程条件变量,Linux 多线程条件变量同步

    条件变量是线程同步的另一种方式,实际上,条件变量是信号量的底层实现,这也就意味着,使用条件变量可以拥有更大的自由度,同时也就需要更加小心的进行同步操作.条件变量使用的条件本身是需要使用互斥量进行保护的 ...

  3. Linux Qt使用POSIX多线程条件变量、互斥锁(量)

    今天团建,但是文章也要写.酒要喝好,文要写美,方为我辈程序员的全才之路.嘎嘎 之前一直在看POSIX的多线程编程,上个周末结合自己的理解,写了一个基于Qt的用条件变量同步线程的例子.故此来和大家一起分 ...

  4. 多线程条件变量(pthread_cond_wait)用法

    条件变量是利用线程间共享得全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起:另一个线程使"条件成立"给出条件成立信号.为了 ...

  5. 多线程---条件变量

    互斥器和条件变量的区别:互斥器具有加锁原语,用来进行排他性的访问共享数据,而条件变量具有等待原语,用于等待某个事件的发生. 等待条件变量的正确姿势: void wait() {mutex.lock() ...

  6. linux 多线程条件变量,linux多线程之条件变量

    假设有共享的资源sum,与之相关联的mutex 是lock_s.假设每个线程对sum的操作很简单的,与sum的状态无关,比如只是sum++.那么只用mutex足够了.程序员只要确保每个线程操作前,取得 ...

  7. Java线程:新特征-条件变量(转)

    条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量.但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含义. 这里的条件和 ...

  8. Java线程详解(16)-条件变量

    条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量.但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含义. 这里的条件和 ...

  9. 【C++】多线程互斥锁、条件变量

    我们了解互斥量和条件变量之前,我们先来看一下为什么要有互斥量和条件变量这两个东西,了解为什么有这两东西之后,理解起来后面的东西就简单很多了!!! 先来看下面这段简单的代码: int g_num = 0 ...

最新文章

  1. 计算机导论excel,[计算机导论实验三Excel.doc
  2. SQL 存储过程 解析XML
  3. pci总线定时协议_汽车总线测试的“解忧杂货店”
  4. Iaas,Paas,Saas三者的区别联系是什么?
  5. 头条终面:写个消息中间件
  6. Core Graphics 定制UIVIew 处理图片
  7. 解决android帮助文档打开慢
  8. java学习笔记------ PrintStream
  9. JS判断手机浏览器是横屏or竖屏
  10. Zookeeper集群部署及报错分析
  11. windows 实用小工具(截图、进程管理)
  12. 3dContactPointAnnotationTool开发日志(八)
  13. 计算机基础教程7 - 数字系统
  14. 问卷星指定内容批量自动填写的Python+Selenium+Pandas+Excel解决方案
  15. dnf丢失clientbase_登录dnf时出现dnfbase.dll的丢失
  16. python无法打开h5权限_求助:python post请求访问不到数据
  17. Dubbo 常见的负载均衡(Load Balance)算法,一起学习一下吧~
  18. 喜大普奔!rgee能用了!R语言也可以使用Google Earth Engine了!
  19. fw150rm刷openwrt固件_今晚把本版几乎所有固件刷了个遍。发现Padavan固件速度吊打OpenWrt,有人知道原因吗?...
  20. ONES 解码:为何数字化是“超级工程”

热门文章

  1. 计算机以太网依赖服务或无法启动,win7 网络和共享中心提示“依赖服务或组无法启动” 解决方法...
  2. 如何运用AI聊天机器人,助推内容营销活动取得成功?
  3. Windows下文件批量重命名bat脚本
  4. 小程序实现活动倒计时功能
  5. Spring框架设计模式分析
  6. 关于冷备份与热备份的对比理解
  7. 移动信号e经常无服务器,手机信号差,从4G突然变成“E”?真实原因是这样的!...
  8. 《深入理解计算机系统》 练习题3.49详解
  9. 书法 | 从零学硬笔,我的三天成长路 2
  10. 安徽省2018工程计价电子版_2018安徽省装饰工程计价定额.pdf