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系统的并行性 ...
随机推荐
- 使用Web Component定义自己的专属网页组件
什么是Web Component Web Component是一套Web浏览器的技术和规范,能够让开发者定制自己的HTML元素 来自MDN的描述: Web Component 是一套不同的技术,允许你 ...
- 【牛客刷题】HJ13 句子逆序
题目链接 题目本身不难,但是牛客的输入样例很坑,因此只好使用bufio来进行输入了: package main import ( "bufio" "fmt" & ...
- Maven经验分享(五)Maven拷贝资源
上一章介绍使用ant拷贝资源,这里介绍maven拷贝资源,使用maven-resources-plugin插件. <plugin> <groupId>org.apache.ma ...
- USACO 2024Feb Silver
https://usaco.org/index.php?page=feb24results 话说 usaco 赛后怎么看成绩啊.为啥 submission 只有代码没有评测结果 T3 交了巨大多次才过 ...
- 解锁强强组合: 使用 Kafka + ClickHouse 快速搭建流数据实时处理平台(DoubleCloud 博客)
我们想要解决的问题 让我们深入一个现实场景: 设想你负责汇总多个销售点系统产生的大量数据.这些数据需要被实时处理并在高级分析仪表板上展示,以提供全面的洞察. 在数据处理领域,速度至关重要.ClickH ...
- 配置mysql数据库主从复制
数据库安装 Step1 先删除data文件 Step2 再根据ini配置文件初始化 mysqld --initialize-insecure --user=mysql Step3 安装mysql服务 ...
- OpenCV开发笔记(七十九):基于Stitcher类实现全景图片拼接
前言 一个摄像头视野不大的时候,我们希望进行两个视野合并,这样让正视的视野增大,从而可以看到更广阔的标准视野.拼接的方法分为两条路,第一条路是stitcher类,第二条思路是特征点匹配. 本篇使 ...
- 为什么C++ 单例局部static初始化是线程安全的?
为什么C++ 单例局部static初始化是线程安全的? const bg::AppSettings& bg::AppSettings::GetInstance() { static AppSe ...
- JavaScript – Sort
前言 排序是很常见的需求. 虽然看似简单, 但其实暗藏杀机. 一不小心就会搞出 Bug 哦. 这篇就来聊聊 JS 的排序. 参考 原生JS数组sort()排序方法内部原理探究 值的比较 js中的loc ...
- ASP.NET Core – Try Preview
前言 .NET 7 已经来到 RC 阶段了. 通常 RC 就是我们 (写库的人) 要入场的时候了. 有发现 Bug 要尽可能在这段期间提交. 不然后患无穷. 这篇主要就是来讲讲如果测试 RC 版本的 ...