IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 280-C++互斥锁 条件变量 -> 正文阅读

[C++知识库]280-C++互斥锁 条件变量

1.如果g_num是原子的,就是说线程a++g_num的时候线程b不能++,线程b++g_num的时候,线程a不能++,所以最后的结果总是为11,而如果g_num不是原子的,那最后的结果有可能是11也有可能不是11

在定义g_num的时候就算加上volatile易变关键字也依然是不正确的
在这里插入图片描述
2.互斥量的头文件是#include

3.互斥量主要解决的是异步中对同一资源的竞争,互斥量的特点是我访问的时候你不能访问,你访问的时候我不能访问

线程a执行到mt.lock()的时候进行加锁,如果线程b此时也执行到mt.lock()了,发现已经加锁了,就阻塞住了,等待线程a执行完++和打印操作并解锁后,就会把线程b从阻塞唤醒到就绪,然后线程b加锁进行一系列操作等等,也有可能线程a循环回来以后发现还没有加锁,可能再一次加锁,所以解锁后线程a和线程b都有可能获得锁

宏观上线程a和线程b一起执行,微观上是交替执行
在这里插入图片描述
4.lock()加锁失败,就会将线程阻塞,而try_lock()失败会返回假,所以try_lock()竞争就非常非常的大,CPU的损耗也比较大,为什么呢?

因为try_lock()并不会将线程阻塞,他会不断的尝试加锁,一直进行while循环

5.互斥量中的拷贝构造函数、赋值函数、移动构造函数、移动赋值函数被删掉了,只有构造函数和析构函数了

6.recursive_mutex递归锁的意思是说,在线程a中获得锁,rmtx.lock()后,调用另一个函数,该函数内也有rmtx.lock(),由于我们已经获得锁这个资源了,就可以继续执行,继续调用该函数,形成递归,当然,回退的时候一个rmtx.lock()应该对应着一个rmtx.unlock()

而普通锁是不能连续锁两次的,会导致程序崩溃

7.lock_guard也是只有构造函数和析构函数

lock_guard是互斥体包装器,为在作用域块期间占用互斥提供便利RALL风格机制,就是对lock()和unlock()做了一个包装,类似于智能指针,是智能指针的风格

std::lock_guardstd::mutex lock(mtx);实际上调用的就是mtx.lock(),当块作用域结束时,会将该对象析构掉,达到了在块作用域lock()和unlock()的功能,就是对lock()和unlock()进行了一个包装,类似于智能指针
在这里插入图片描述
8.scoped_lock用于多个互斥体的免死锁RAII封装器,主要处理死锁的问题

9.条件变量是允许多个线程相互交流的同步原语,可以实现线程同步,它允许一定量的线程等待另一线程的提醒,然后再继续,条件变量始终关联到一个互斥

同步就是若干个线程可以有次序的执行

10.wait导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选的循环直至满足某谓词,这个谓词指的就是bool值,真和假

11.假如有三个线程,三个线程分别打印A、B、C,想要三个线程循环打印ABCABC…

必须有互斥量,条件变量,全局变量才能达到线程同步的目的

条件变量wait函数的特点:①阻塞当前线程,由就绪状态转为阻塞状态②把拥有锁的资源释放掉③获得互斥锁

假如先执行的是print_B()函数,先获得互斥锁,然后进入while循环,isReady!=1的值为真,执行wait函数,阻塞当前线程并将锁资源释放,将线程B放入阻塞队列中,假如然后执行的是print_C()函数,也是先获得锁(因为线程B在wait时将锁资源释放了,所以print_C()函数可以正常获得锁),进入while循环,isReady!=1的值为真,执行wait函数,阻塞当前线程并将锁资源释放,将线程C放入阻塞队列中,假如然后执行print_A()函数,获得锁,进入while循环,isReady!=0为假,不进入循环,打印出A,并将条件变量isReady的值改为1,并唤醒阻塞队列中所有等待的线程,然后再次进入while循环,此时isReady!=0为真,阻塞当前线程并将锁资源释放,将线程A放入阻塞队列中,以此类推可以循环打印ABCABC…

实际上线程之间是抢夺互斥锁的,谁抢上了就执行哪个线程
在这里插入图片描述
惊群现象:notify_all唤醒阻塞队列中所有等待的线程,而阻塞队列中可能有很多很多线程,就会导致所有线程都去争夺锁的资源,但是最终只能有一个线程获得锁

解决惊群现象可以使用notify_one唤醒一个线程,但是并不确定唤醒的是哪一个线程,如果唤醒的是不想唤醒的线程,就会导致白白被唤醒,所以如何精准唤醒是很重要的,就是A去精准唤醒B,B去精准唤醒C,C再精准唤醒A

12.条件变量的底层有一个等待队列

13.唤醒并不意味着一定会从等待队列中取出,必须要获得锁资源后才能从等待队列中取出

14.如果将里面的notify_all都改为notify_one会怎么样?

有可能会正常打印,比如说先运行a,然后运行b,然后运行c

也有可能就死掉了,比如先运行c,c不满足,将c放入等待队列中,然后运行b,b也不满足,将b放入等待队列中,然后运行a,a满足,唤醒bc其中的一个线程,并将a放入等待队列中,假如唤醒的是c,c并不满足条件,将c放入等待队列中,此时abc都在等待队列中,没有线程可以去执行notify_one
在这里插入图片描述
15.wait函数也可以使用lambda表达式,lambda表达式真退出假等待

16.锁的粒度

假如有两个线程,对于Mysql数据库的表来说,如果它有一个表锁,那么线程1获得表锁的以后,线程2就不能获得表锁,必须等待线程1释放表锁后才能获得,而如果每一条记录都有一个锁,就是行锁,只有当线程1和线程2都访问同一条记录时,才能阻塞,否则就可以同时对表进行操作

锁的粒度越小,并发性就越高,但是耗损的资源就越多,锁的粒度越大,并发性就越低,但是耗损的资源就越少

所以要在粒度和并发性上面有一个平衡

17.条件变量头文件#include <condition_variable>

18.死锁,就是出现一个我等你你等我的状态

线程1获得资源1,然后睡眠一会,线程2获得资源2,然后睡眠一会,这时线程1希望获得资源2,但是此时资源2被线程2占用,就会等待线程2释放资源,同样线程1希望获得资源1,但是此时资源1被线程1占用,就会等待线程1释放资源,这就是死锁,出现我等你你等我的情况

程序中睡眠的意思是说,在其中进行了一些操作,拿睡眠来代替这些操作

产生死锁的原因:互斥、占有等待、不可抢占、循环等待

这个程序就是因为占有等待导致死锁
在这里插入图片描述
19.两个账户转账问题
在这里插入图片描述

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:05:26  更:2022-03-30 18:06:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:04:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码