C++中的各种锁
在多线程开发中,经常会遇到数据同步,很多情况下用锁都是一个很好的选择。C++中常用的锁主要有下面几种:
互斥锁(std::mutex
)
- 这是最基本的一种锁。它用于保护共享资源,在任意时刻,最多只有一个线程可以获取该锁,从而访问被保护的资源。当一个线程获取了互斥锁后,其他试图获取该锁的线程会被阻塞,直到持有锁的线程释放它。
- 例如,在一个多线程程序中,如果多个线程需要访问和修改同一个全局变量,就可以使用互斥锁来确保在同一时间只有一个线程能够进行修改操作,避免数据竞争导致的错误结果。
1 #include <iostream>
2 #include <mutex>
3 #include <thread>
4
5 std::mutex m;
6 int counter = 0;
7
8 void increment() {
9 m.lock();
10 counter++;
11 std::cout << "Counter value in thread " << std::this_thread::get_id() << " is " << counter << std::endl;
12 m.unlock();
13 }
14
15 int main() {
16 std::thread t1(increment);
17 std::thread t2(increment);
18 t1.join();
19 t2.join();
20 return 0;
21 }
递归互斥锁(std::recursive_mutex
)
- 递归互斥锁允许同一个线程多次获取该锁。它内部会记录锁的获取次数,每获取一次,计数加 1,每次释放锁时,计数减 1,当计数为 0 时,锁才真正被释放,可供其他线程获取。
- 假设在一个复杂的函数调用链中,函数 A 调用函数 B,函数 B 又调用函数 A,并且这些函数都需要访问同一个受保护的资源。如果使用普通互斥锁,就会出现死锁,而递归互斥锁就可以避免这种情况,因为它允许同一线程多次获取锁。
1 #include <iostream>
2 #include <mutex>
3 #include <thread>
4
5 std::recursive_mutex rm;
6
7 void recursiveFunction(int count) {
8 rm.lock();
9 if (count > 0) {
10 std::cout << "Recursive call with count = " << count << std::endl;
11 recursiveFunction(count - 1);
12 }
13 rm.unlock();
14 }
15
16 int main() {
17 std::thread t(recursiveFunction, 3);
18 t.join();
19 return 0;
20 }
读写锁(std::shared_mutex
) C++17开始才有
- 读写锁主要用于区分对共享资源的读操作和写操作。它有两种获取模式:共享模式(读模式)和独占模式(写模式)。
- 多个线程可以同时以共享模式获取读写锁,这意味着它们可以同时读取共享资源,而不会相互干扰。但是,当一个线程要以独占模式获取读写锁(进行写操作)时,其他线程(无论是读操作还是写操作)都不能获取该锁,直到写操作完成并释放锁。这种机制在有大量读操作和少量写操作的场景下,可以提高程序的并发性能。例如,在一个缓存系统中,多个线程可能经常读取缓存中的数据,只有在缓存数据需要更新时才会进行写操作,使用读写锁可以很好地处理这种情况。
1 #include <iostream>
2 #include <shared_mutex>
3 #include <thread>
4 #include <vector>
5
6 std::shared_mutex smtx;
7 int shared_data = 0;
8
9 void read_data() {
10 std::shared_lock<std::shared_mutex> lock(smtx);
11 std::cout << "Read data: " << shared_data << std::endl;
12 }
13
14 void write_data(int new_value) {
15 std::unique_lock<std::shared_mutex> lock(smtx);
16 shared_data = new_value;
17 std::cout << "Wrote data: " << shared_data << std::endl;
18 }
19
20 int main() {
21 std::vector<std::thread> read_threads;
22 for (int i = 0; i < 5; i++) {
23 read_threads.push_back(std::thread(read_data));
24 }
25 std::thread write_thread(write_data, 10);
26 for (auto& t : read_threads) {
27 t.join();
28 }
29 write_thread.join();
30 return 0;
31 }
定时互斥锁(std::timed_mutex
)
- 定时互斥锁是
std::mutex
的扩展。除了具备std::mutex
的基本功能外,它还允许线程在尝试获取锁时设置一个超时时间。 - 如果在规定的超时时间内无法获取锁,线程不会一直等待,而是可以执行其他操作或者返回错误信息。这在一些对时间敏感的场景中非常有用,比如在一个实时系统中,线程不能因为等待一个锁而无限期地阻塞,需要在一定时间后放弃获取并进行其他处理。
1 #include <iostream>
2 #include <chrono>
3 #include <thread>
4 #include <mutex>
5
6 std::timed_mutex tm;
7
8 void tryLockFunction() {
9 if (tm.try_lock_for(std::chrono::seconds(1))) {
10 std::cout << "Acquired lock" << std::endl;
11 std::this_thread::sleep_for(std::chrono::seconds(2));
12 tm.unlock();
13 } else {
14 std::cout << "Could not acquire lock in time" << std::endl;
15 }
16 }
17
18 int main() {
19 std::thread t1(tryLockFunction);
20 std::thread t2(tryLockFunction);
21 t1.join();
22 t2.join();
23 return 0;
24 }
递归定时互斥锁(std::recursive_timed_mutex
)
- 这是结合了递归互斥锁和定时互斥锁特点的一种锁。它允许同一线程多次获取锁,并且在获取锁时可以设置超时时间。
- 当一个线程多次获取这种锁后,需要释放相同次数的锁,锁才会真正被释放,并且在获取锁的过程中,如果在超时时间内无法获取,线程可以采取相应的措施。
1 #include <iostream>
2 #include <chrono>
3 #include <thread>
4 #include <mutex>
5
6 std::recursive_timed_mutex rtm;
7
8 void recursiveTryLockFunction(int count) {
9 if (rtm.try_lock_for(std::chrono::seconds(1))) {
10 std::cout << "Recursive acquired lock, count = " << count << std::endl;
11 if (count > 0) {
12 recursiveTryLockFunction(count - 1);
13 }
14 rtm.unlock();
15 } else {
16 std::cout << "Could not recursively acquire lock in time" << std::endl;
17 }
18 }
19
20 int main() {
21 std::thread t(recursiveTryLockFunction, 3);
22 t.join();
23 return 0;
24 }
自旋锁(通常用std::atomic_flag
实现)
- 自旋锁是一种忙等待的锁机制。当一个线程尝试获取自旋锁而锁已经被占用时,这个线程不会进入阻塞状态,而是会不断地检查(“自旋”)锁是否已经被释放。
- 自旋锁在等待时间较短的情况下可能会有比较好的性能表现,因为它避免了线程切换的开销。但是,如果等待时间过长,由于线程一直在占用 CPU 资源进行检查,会导致 CPU 资源的浪费。一般在底层代码或者对性能要求极高、等待时间预计很短的场景下使用。
1 #include <iostream>
2 #include <atomic>
3 #include <thread>
4
5 std::atomic_flag spinLock = ATOMIC_FLAG_INIT;
6
7 void criticalSection() {
8 while (spinLock.test_and_set()) {
9 // 自旋等待
10 }
11 std::cout << "Entered critical section" << std::endl;
12 // 临界区操作
13 spinLock.clear();
14 }
15
16 int main() {
17 std::thread t1(criticalSection);
18 std::thread t2(criticalSection);
19 t1.join();
20 t2.join();
21 return 0;
22 }
条件变量(std::condition_variable
)配合互斥锁用于同步(严格来说条件变量不是锁,但常一起用于线程同步场景)
- 条件变量本身不是一种锁,但它通常和互斥锁一起使用,用于实现线程间的同步。它可以让一个线程等待某个条件满足后再继续执行。
- 例如,一个生产者 - 消费者模型中,消费者线程在缓冲区为空时可以使用条件变量等待,直到生产者线程生产出产品并通知消费者线程,这个过程中互斥锁用于保护缓冲区这个共享资源,条件变量用于实现线程间的通信和同步。
1 #include <iostream>
2 #include <thread>
3 #include <mutex>
4 #include <condition_variable>
5 #include <queue>
6
7 std::mutex mtx;
8 std::condition_variable cv;
9 std::queue<int> buffer;
10 const int bufferSize = 5;
11
12 void producer() {
13 for (int i = 0; i < 10; ++i) {
14 std::unique_lock<std::mutex> lock(mtx);
15 while (buffer.size() == bufferSize) {
16 cv.wait(lock);
17 }
18 buffer.push(i);
19 std::cout << "Produced: " << i << std::endl;
20 cv.notify_all();
21 }
22 }
23
24 void consumer() {
25 for (int i = 0; i < 10; ++i) {
26 std::unique_lock<std::mutex> lock(mtx);
27 while (buffer.empty()) {
28 cv.wait(lock);
29 }
30 int data = buffer.front();
31 buffer.pop();
32 std::cout << "Consumed: " << data << std::endl;
33 cv.notify_all();
34 }
35 }
36
37 int main() {
38 std::thread producerThread(producer);
39 std::thread consumerThread(consumer);
40 producerThread.join();
41 consumerThread.join();
42 return 0;
43 }
C++中的各种锁的更多相关文章
- Java中的显示锁 ReentrantLock 和 ReentrantReadWriteLock
在Java1.5中引入了两种显示锁,分别是可重入锁ReentrantLock和可重入读写锁ReentrantReadWriteLock.它们分别实现接口Lock和ReadWriteLock.(注意:s ...
- 在 Java 中高效使用锁的技巧--转载
竞争锁是造成多线程应用程序性能瓶颈的主要原因 区分竞争锁和非竞争锁对性能的影响非常重要.如果一个锁自始至终只被一个线程使用,那么 JVM 有能力优化它带来的绝大部分损耗.如果一个锁被多个线程使用过,但 ...
- 分门别类总结Java中的各种锁,让你彻底记住
概念 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁. 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁.有可能,会造成优先级反转或者饥 ...
- SQLSERVER中的元数据锁
SQLSERVER中的元数据锁 网上对于元数据锁的资料真的非常少 元数据锁一般会出现在DDL语句里 下面列出数据库引擎可以锁定的资源 资源 说明 RID 用于锁定堆(heap)中的某一行 KEY 用于 ...
- 编写高质量代码改善C#程序的157个建议——建议89:在并行方法体中谨慎使用锁
建议89:在并行方法体中谨慎使用锁 除了建议88所提到的场合,要谨慎使用并行的情况还包括:某些本身就需要同步运行的场合,或者需要较长时间锁定共享资源的场合. 在对整型数据进行同步操作时,可以使用静态类 ...
- Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等
Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...
- Java中的各种锁--分类总结
前言 本文需要具备一定的多线程基础才能更好的理解. 学习java多线程时,最头疼的知识点之一就是java中的锁了,什么互斥锁.排它锁.自旋锁.死锁.活锁等等,细分的话可以罗列出20种左右的锁,光是看着 ...
- mysql中的乐观锁和悲观锁
mysql中的乐观锁和悲观锁的简介以及如何简单运用. 关于mysql中的乐观锁和悲观锁面试的时候被问到的概率还是比较大的. mysql的悲观锁: 其实理解起来非常简单,当数据被外界修改持保守态度,包括 ...
- 轻松搞懂Java中的自旋锁
前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...
- 浅谈Linux中的各种锁及其基本原理
本文首发于:https://mp.weixin.qq.com/s/Ahb4QOnxvb2RpCJ3o7RNwg 微信公众号:后端技术指南针 0.概述 通过本文将了解到如下内容: Linux系统的并行性 ...
随机推荐
- NVIDIA vGPU vApps/vWS/vCS适配GPU版本介绍
NVIDIA vGPU 12.0版本-vGPU版本名称变化 - 注: 2021年1月生效 最新名称 NVIDIA Virtual PC (vPC) -曾用名称 NVIDIA GRID Virtual ...
- 【CMake系列】11-CMake Pack
cmake pack 用于将我们的写好的项目 打包,发送给使用方:打包后产生的内容有 源代码包 二进制包 平台原生的二进制安装 Debian -> .deb red hat -> .rpm ...
- k8s批量操作
批量删除Evicted状态pod kubectl -n gitee get pods | grep Evicted |awk '{print$1}'|xargs kubectl -n gitee de ...
- 安装部署harbor
配置文件中修改内容hostname 和 证书 私钥 需要指定到文件 hostname: hub.rainsc.com # http related config http: # port for ht ...
- Apple Silicon 芯片 Mac 在 x86_64 模式下启动 Kettle
苹果于 2020 年推出了自家设计的基于 ARM 架构的 M1 芯片,在日常生活的大部分使用过程中,M1 的体验很好.然而,依然存在一小部分软件无法兼容 ARM 架构,需要我们模拟 x86 的架构来运 ...
- 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后;在k1值相同的情况下,再看k2,k2值小的在前,大的在后。满足这种要求的
题目: 设线性表中每个元素有两个数据项k1和k2,现对线性表按一下规则进行排序:先看数据项k1,k1值小的元素在前,大的在后:在k1值相同的情况下,再看k2,k2值小的在前,大的在后.满足这种要求的排 ...
- cesium中添加建筑白模
1.在cesium中添加模型依赖于Cesium ion帐户的资产id,在这里创建账户. 2.上传模型(模型文件类型在Cesium ion中有说明,模型的提取办法可在这里查看)到账户中并平铺为3D Ti ...
- PTA甲级—图
1.图的遍历 1013 Battle Over Cities (25 分) 一种方法使用dfs求连通块的个数 #include <cstdio> #include <cstring& ...
- DDL-操作表
1.查询表 查询当前数据库下所有表名称 show tables; 查询表结构 desc 表名称; 2.创建表 create table 表名 ( 字段名1 数据类型1, 字段名2 数据类型2, ... ...
- 图解MQTT概念、mosquitto编译和部署 ,写代码,分别使用外网和本地服务器进行测试
前沿提要: MQTT是什么不知道? 看这一篇:https://www.cnblogs.com/happybirthdaytoyou/p/10362336.html 阿里云官网玩不转? 看这一篇: ht ...