C++线程中的几种锁
线程之间的锁有:互斥锁、条件锁、自旋锁、读写锁、递归锁。一般而言,锁的功能越强大,性能就会越低。
1、互斥锁
互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量。也就是说是为了避免多个线程在某一时刻同时操作一个共享资源。例如线程池中的有多个空闲线程和一个任务队列。任何是一个线程都要使用互斥锁互斥访问任务队列,以避免多个线程同时访问任务队列以发生错乱。
在某一时刻,只有一个线程可以获取互斥锁,在释放互斥锁之前其他线程都不能获取该互斥锁。如果其他线程想要获取这个互斥锁,那么这个线程只能以阻塞方式进行等待。
头文件:<pthread.h>
类型:pthread_mutex_t,
函数:pthread_mutex_init(pthread_mutex_t * mutex, const phtread_mutexattr_t * mutexattr);//动态方式创建锁,相当于new动态创建一个对象
pthread_mutex_destory(pthread_mutex_t *mutex)//释放互斥锁,相当于delete
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//以静态方式创建锁
pthread_mutex_lock(pthread_mutex_t *mutex)//以阻塞方式运行的。如果之前mutex被加锁了,那么程序会阻塞在这里。
pthread_mutex_unlock(pthread_mutex_t *mutex)
int pthread_mutex_trylock(pthread_mutex_t * mutex);//会尝试对mutex加锁。如果mutex之前已经被锁定,返回非0,;如果mutex没有被锁定,则函数返回并锁定mutex
//该函数是以非阻塞方式运行了。也就是说如果mutex之前已经被锁定,函数会返回非0,程序继续往下执行。
2、条件锁
条件锁就是所谓的条件变量,某一个线程因为某个条件为满足时可以使用条件变量使改程序处于阻塞状态。一旦条件满足以“信号量”的方式唤醒一个因为该条件而被阻塞的线程。最为常见就是在线程池中,起初没有任务时任务队列为空,此时线程池中的线程因为“任务队列为空”这个条件处于阻塞状态。一旦有任务进来,就会以信号量的方式唤醒一个线程来处理这个任务。这个过程中就使用到了条件变量pthread_cond_t。
头文件:<pthread.h>
类型:pthread_cond_t
函数:pthread_cond_init(pthread_cond_t * condtion, const phtread_condattr_t * condattr);//对条件变量进行动态初始化,相当于new创建对象
pthread_cond_destory(pthread_cond_t * condition);//释放动态申请的条件变量,相当于delete释放对象
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;//静态初始化条件变量
pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);//该函数以阻塞方式执行。如果某个线程中的程序执行了该函数,那么这个线程就会以阻塞方式等待,直到收到pthread_cond_signal或者pthread_cond_broadcast函数发来的信号而被唤醒。
注意:pthread_cond_wait函数的语义相当于:首先解锁互斥锁,然后以阻塞方式等待条件变量的信号,收到信号后又会对互斥锁加锁。
为了防止“虚假唤醒”,该函数一般放在while循环体中。例如
- pthread_mutex_lock(mutex);//加互斥锁
- while(条件不成立)//当前线程中条件变量不成立
- {
- pthread_cond_wait(cond, mutex);//解锁,其他线程使条件成立发送信号,加锁。
- }
- ...//对进程之间的共享资源进行操作
- pthread_mutex_unlock(mutex);//释放互斥锁
pthread_cond_signal(pthread_cond_t * cond);//在另外一个线程中改变线程,条件满足发送信号。唤醒一个等待的线程(可能有多个线程处于阻塞状态),唤醒哪个线程由具体的线程调度策略决定
pthread_cond_broadcast(pthread_cond_t * cond);//以广播形式唤醒所有因为该条件变量而阻塞的所有线程,唤醒哪个线程由具体的线程调度策略决定
pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, struct timespec * time);//以阻塞方式等待,如果时间time到了条件还没有满足还是会结束
3、自旋锁
前面的两种锁是比较常见的锁,也比较容易理解。下面通过比较互斥锁和自旋锁原理的不同,这对于真正理解自旋锁有很大帮助。
假设我们有一个两个处理器core1和core2计算机,现在在这台计算机上运行的程序中有两个线程:T1和T2分别在处理器core1和core2上运行,两个线程之间共享着一个资源。
首先我们说明互斥锁的工作原理,互斥锁是是一种sleep-waiting的锁。假设线程T1获取互斥锁并且正在core1上运行时,此时线程T2也想要获取互斥锁(pthread_mutex_lock),但是由于T1正在使用互斥锁使得T2被阻塞。当T2处于阻塞状态时,T2被放入到等待队列中去,处理器core2会去处理其他任务而不必一直等待(忙等)。也就是说处理器不会因为线程阻塞而空闲着,它去处理其他事务去了。
而自旋锁就不同了,自旋锁是一种busy-waiting的锁。也就是说,如果T1正在使用自旋锁,而T2也去申请这个自旋锁,此时T2肯定得不到这个自旋锁。与互斥锁相反的是,此时运行T2的处理器core2会一直不断地循环检查锁是否可用(自旋锁请求),直到获取到这个自旋锁为止。
从“自旋锁”的名字也可以看出来,如果一个线程想要获取一个被使用的自旋锁,那么它会一致占用CPU请求这个自旋锁使得CPU不能去做其他的事情,直到获取这个锁为止,这就是“自旋”的含义。
当发生阻塞时,互斥锁可以让CPU去处理其他的任务;而自旋锁让CPU一直不断循环请求获取这个锁。通过两个含义的对比可以我们知道“自旋锁”是比较耗费CPU的
头文件:<linux\spinlock.h>
自旋锁的类型:spinlock_t
相关函数:初始化:spin_lock_init(spinlock_t *x);
spin_lock(x); //只有在获得锁的情况下才返回,否则一直“自旋”
spin_is_locked(x)// 该宏用于判断自旋锁x是否已经被某执行单元保持(即被锁),如果是, 返回真,否则返回假。
注意:自旋锁适合于短时间的的轻量级的加锁机制。
4、读写锁
说到读写锁我们可以借助于“读者-写者”问题进行理解。首先我们简单说下“读者-写者”问题。
计算机中某些数据被多个进程共享,对数据库的操作有两种:一种是读操作,就是从数据库中读取数据不会修改数据库中内容;另一种就是写操作,写操作会修改数据库中存放的数据。因此可以得到我们允许在数据库上同时执行多个“读”操作,但是某一时刻只能在数据库上有一个“写”操作来更新数据。这就是一个简单的读者-写者模型。
C++线程中的几种锁的更多相关文章
- iOS中的几种锁的总结,三种开启多线程的方式(GCD、NSOperation、NSThread)
学习内容 欢迎关注我的iOS学习总结--每天学一点iOS:https://github.com/practiceqian/one-day-one-iOS-summary OC中的几种锁 为什么要引入锁 ...
- C#中的几种锁:用户模式锁、内核模式锁、动态计数、监视锁
参考网址: https://blog.csdn.net/weixin_43989331/article/details/105356008 C#中的几种锁:用户模式锁.内核模式锁.动态计数.监视锁介绍 ...
- 线程中的wait() 与 锁的关系
我们先看一段代码: /** * 计算输出其他线程锁计算的数据 * */ public class ThreadA { public static void main(String[] args) th ...
- 同步中的四种锁synchronized、ReentrantLock、ReadWriteLock、StampedLock
目录 1.synchronized同步锁 2.ReentrantLock重入锁 3.ReadWriteLock读写锁 4.StampedLock戳锁(目前没找到合适的名字,先这么叫吧...) 5.总结 ...
- 同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock
为了更好的支持并发程序,JDK内部提供了多种锁.本文总结4种锁. 1.synchronized同步锁 使用: synchronized本质上就2种锁: 1.锁同步代码块 2.锁方法 可用object. ...
- iOS: 线程中那些常见的锁
一.介绍 在多线程开发中,锁的使用基本必不可少,主要是为了解决资源共享时出现争夺而导致数据不一致的问题,也就是线程安全问题.锁的种类很多,在实际开发中,需要根据情况选择性的选取使用,毕竟使用锁也是消耗 ...
- java中的12种锁
java中很多地方会涉及到锁,比如java代码并发场景,DB中的并发场景,分布式中的锁....你知道几种呢?下面来看看常见的11种锁 1. 乐观锁/悲观锁 这两个概念是人们对java中各种锁总结提出的 ...
- 全面了解Java中的15种锁概念及机制!
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 1.公平锁 / 非公平锁 2.可重入锁 / 不可重入锁 3.独享锁 / 共享锁 4.互斥锁 / 读 ...
- 5000字 | 24张图带你彻底理解Java中的21种锁
本篇主要内容如下: 本篇文章已收纳到我的Java在线文档. Github 我的SpringCloud实战项目持续更新中 帮你总结好的锁: 序号 锁名称 应用 1 乐观锁 CAS 2 悲观锁 synch ...
随机推荐
- 【Android O】 Service AAA does not have a SELinux domain defined
在init.AAA.rc里面添加了一个脚本启动的服务: service AAA /vendor/bin/sh /vendor/etc/AAA_spec.sh user root group root ...
- Linux下C语言生成可执行文件的过程
在当前目录下创建一个C源文件并打开: touch test.c gedit test.c直接编译: gcc test.c -o test 分步骤编译: 1) 预处理 gcc -E test.c ...
- [BZOJ 2285] [SDOI 2011] 保密
Description 传送门 Solution 这道题的最大难点在于读懂题意(雾 分数规划求出 \(n\) 到 \(1\cdots n_1\) 每个点的最小 \(\sum\frac{t_i}{s_i ...
- 腾讯笔试---小Q的歌单
链接:https://www.nowcoder.com/questionTerminal/f3ab6fe72af34b71a2fd1d83304cbbb3 来源:牛客网 小Q有X首长度为A的不同的歌和 ...
- Dubbo2.6.5+Nacos注册中心(代替Zookeeper)
在上一节的小栗子的基础上,只需要更改两个地方 第一个:父工程的pom依赖增加 <!-- Dubbo Nacos registry dependency --> <dependency ...
- (二分查找 拓展) leetcode278. First Bad Version
You are a product manager and currently leading a team to develop a new product. Unfortunately, the ...
- saltstack主机管理项目:编写插件基类-获取主机列表-提取yaml配置文件(四)
一.编写插件基类 1.目录结构 1.我是如何获知我有多少种系统? 当客户端第一连接过来的时候,我就已经把这些文件存下来了 ,存在到哪里了?存到数据库了 每次对主机发送命令的动作时,我从库里把数据取出来 ...
- CMDB服务器管理系统【s5day88】:采集资产-文件配置(二)
上节疑问: 1.老师我们已经写到global_settings里了,为什么还要写到__init__.py setting 这的作用是为了:整合起两个的组合global_settings和setting ...
- EF CodeFirst系列(2)---CodeFirst的数据库初始化
1. CodeFirst的默认约定 1.领域类和数据库架构的映射约定 在介绍数据库的初始化之前我们需要先了解领域类和数据库之间映射的一些约定.在CodeFirst模式中,约定指的是根据领域类(如Stu ...
- EF CodeFirst系列(9)---添加初始化数据和数据库迁移策略
1.添加初始化数据(Seed) 我们可以在初始化数据库的过程中给数据库添加一些数据.为了实现初始化数据(seed data)我们必须创建一个自定义的数据库初始化器(DB initializer),并重 ...