news 2026/6/10 11:29:25

C++ 互斥锁、读写锁、原子操作、条件变量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ 互斥锁、读写锁、原子操作、条件变量

前言:在多线程的实现中,对临界资源的访问容易产生冲突与竞争。C++提供了一些方法来解决这种资源冲突,如,互斥锁、读写锁、原子操作、条件变量。本文将对这四种方式进行一一介绍。

目录

一、互斥锁(std::mutex)

1.1 原理

1.2 C++库

1.3 代码示例

1.4 注意事项

二、读写锁(std::shared_mutex)

2.1 原理

2.2 C++库

2.3 代码示例

三、原子操作(std::atomic)

3.1 原理

3.2 C++库

3.3 代码示例

3.4 注意事项

四、条件变量(std::condition_variable)

4.1 原理

4.2 C++库

4.3 代码示例

4.4 注意事项


一、互斥锁(std::mutex)

1.1 原理

互斥锁是最基础的排他性同步语言,其核心逻辑是:同一时刻仅允许一个线程持有锁,其他线程尝试加锁时会阻塞或失败。

1.2 C++库

类型特点
std::mutex基础互斥锁,非递归、无超时,加锁失败则阻塞
std::recursive_mutex递归互斥锁,允许同一线程多次加锁(需对应次数解锁)
std::timed_mutex带超时的互斥锁,支持try_lock_for/try_lock_until(超时返回 false)

1.3 代码示例

#include <iostream> #include <mutex> #include <thread> #include <vector> std::mutex mtx; int counter = 0; // 共享资源 // 临界区操作:自增计数器 void increment(int n) { for (int i = 0; i < n; ++i) { // 推荐RAII:lock_guard(作用域结束自动解锁,避免死锁) std::lock_guard<std::mutex> lock(mtx); counter++; // 临界区(独占访问) } } int main() { std::vector<std::thread> threads; for (int i = 0; i < 4; ++i) { threads.emplace_back(increment, 100000); } for (auto& t : threads) t.join(); std::cout << "最终计数器:" << counter << std::endl; // 预期400000 return 0; }

1.4 注意事项

避免死锁,遵循“加锁顺序一致、避免嵌套锁”。

二、读写锁(std::shared_mutex)

2.1 原理

读写锁是互斥锁的优化版,核心思想是:读共享,写排他。即,多个线程可同时持有读锁,但写锁阻塞所有读锁,写操作独占。适合“读多写少”的情况(如缓存配置、日志查询)。

2.2 C++库

C++17引入std::shared_mutex

2.3 代码示例

#include <iostream> #include <shared_mutex> #include <thread> #include <vector> std::shared_mutex rw_mtx; int cache_data = 0; // 读多写少的共享缓存 // 读线程:共享读锁(多线程同时读) void read_cache(int id) { std::shared_lock<std::shared_mutex> lock(rw_mtx); // 加读锁 std::cout << "读线程" << id << "读取:" << cache_data << std::endl; } // 写线程:排他写锁(独占) void update_cache(int val) { std::lock_guard<std::shared_mutex> lock(rw_mtx); // 加写锁 cache_data = val; std::cout << "写线程更新为:" << val << std::endl; } int main() { std::vector<std::thread> threads; // 5个读线程(同时执行) for (int i = 0; i < 5; ++i) threads.emplace_back(read_cache, i); // 1个写线程(阻塞所有读线程) threads.emplace_back(update_cache, 100); for (auto& t : threads) { t.join(); } return 0; }

三、原子操作(std::atomic)

3.1 原理

原子操作是无锁同步,其原理是:通过CPU指令级原子性(如lock前缀)实现,无需内核调度,适用于简单变量的“读 - 改 - 写”(如计数器、标志位等)。

3.2 C++库

C++11引入std::atomic模板,支持基本数据类型,方法调用包括:

操作作用原子性
operator++/operator+=自增 / 加法赋值
fetch_add(n)加 n 并返回旧值
load()/store(v)读取 / 写入值(内存序可控)
compare_exchange_weak/strongCAS(比较并交换)

3.3 代码示例

#include <iostream> #include <atomic> #include <thread> #include <vector> std::atomic<int> atomic_counter(0); // 原子计数器 void atomic_increment(int n) { for (int i = 0; i < n; ++i) { atomic_counter++; // 原子自增(无锁) // 等价于:atomic_counter.fetch_add(1, std::memory_order_relaxed); } } // 高级:CAS实现无锁更新 void cas_demo() { int expected = 0; int new_val = 100; // 弱CAS(可能虚假失败,需循环) while (!atomic_counter.compare_exchange_weak(expected, new_val)) { std::cout << "CAS失败,当前值:" << expected << std::endl; } std::cout << "CAS成功,值:" << atomic_counter << std::endl; } int main() { std::vector<std::thread> threads; for (int i = 0; i < 4; ++i) threads.emplace_back(atomic_increment, 100000); for (auto& t : threads) t.join(); std::cout << "原子计数器:" << atomic_counter << std::endl; // 400000 cas_demo(); return 0; }

3.4 注意事项

仅适合简单变量操作,复杂逻辑仍需要加锁。

四、条件变量(std::condition_variable)

4.1 原理

条件变量解决线程间时序依赖(生产者生产后消费者才能消费),其核心逻辑是:线程A(等待方)阻塞等待条件满足,释放持有的互斥锁,线程B(通知方)满足条件后唤醒等待线程,重新竞争互斥锁。必须与互斥锁配合使用。

4.2 C++库

C++11提供std::consition_variable(仅配合std::unique_lock)与std::condition_variable_any(配合任意类型锁)。

4.3 代码示例

#include <iostream> #include <condition_variable> #include <mutex> #include <thread> #include <queue> std::mutex mtx; std::condition_variable cv; std::queue<int> q; // 共享队列(临界资源) // 生产者:生产数据,通知消费者 void producer(int n) { for (int i = 0; i < n; ++i) { std::unique_lock<std::mutex> lock(mtx); // 加锁 q.push(i); // 生产数据 std::cout << "生产者生产:" << i << std::endl; lock.unlock(); // 可选:提前解锁,减少消费者等待 cv.notify_one(); // 唤醒一个等待的消费者 std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } // 消费者:等待数据,消费 void consumer() { while (true) { std::unique_lock<std::mutex> lock(mtx); // 循环等待:防止虚假唤醒 cv.wait(lock, []() { return !q.empty(); }); // 条件满足,消费数据 int val = q.front(); q.pop(); std::cout << "消费者消费:" << val << std::endl; lock.unlock(); if (val == 9) break; // 消费完退出 } } int main() { std::thread prod(producer, 10); std::thread cons(consumer); prod.join(); cons.join(); return 0; }

4.4 注意事项

std::condition_variable仅支持std::unique_lock,需要手动控制锁的释放。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 10:55:56

QWebEngine 常用 API 全面梳理

公众号:cpp手艺人 QWebEngine 常用 API 全面梳理(超全版本) Qt WebEngine 基于 Chromium,但提供了 Qt 风格的 API。本文对 QWebEngine 的常用类与 API 进行系统梳理,帮助你快速掌握其开发全景。 1. QWebEngineView(视图层) QWebEngineView 是最常用的 UI 控件,主要…

作者头像 李华
网站建设 2026/6/10 16:17:36

探索 COMSOL 光学与相场模拟的奇妙世界

COMSOL光学模型&#xff1a;随机分布颗粒散射&#xff0c;COMSOL光学仿真模型:光镊/光力模型&#xff08;包含三个模型&#xff0c;近似算法&#xff0c;张量算法&#xff09;相场模拟——合金&#xff0c;金属凝固模型&#xff0c;各向异性枝晶生长karma合金凝固模型&#xff…

作者头像 李华
网站建设 2026/6/9 23:33:41

【Linux网络编程】UDP Socket

前言:最近在复习 Linux 网络编程,重点梳理了 UDP 协议的实现细节。虽然 UDP 是无连接、不可靠的协议,但其简单高效的特性在很多场景下(如实时音视频、DNS)依然是首选。从最简单的 Echo Server 出发,逐步重构为支持业务解耦的字典服务器,最后实现一个支持多线程的全双工聊…

作者头像 李华
网站建设 2026/6/10 4:58:56

支持128K上下文的Qwen3-32B究竟有多强?

支持128K上下文的Qwen3-32B究竟有多强&#xff1f; 在当前大模型竞争进入“深水区”的背景下&#xff0c;参数规模的增长已不再是唯一的胜负手。越来越多的企业和开发者开始关注一个更实际的问题&#xff1a;模型能否真正理解并处理现实世界中的复杂信息&#xff1f; 毕竟&…

作者头像 李华
网站建设 2026/6/6 12:42:07

软件第三方测试报告 第三方专业软件测评机构贤诚测评

软件第三方测试报告&#xff1a;为你的系统做一次独立“体检” 想象一下&#xff0c;当你买了一栋房子&#xff0c;开发商说“质量是没问题”&#xff0c;你会在没有专业验房师检查的情况下就签字交款吗&#xff1f;大概不会。同样道理&#xff0c;当一款软件系统开发完成后&am…

作者头像 李华
网站建设 2026/6/9 23:31:02

跨界学习:测试与开发的融合

在快速迭代的软件开发浪潮中&#xff0c;测试与开发的关系正经历深刻重塑。传统的“测试在后、开发在前”的线性模式已难以应对市场对速度与质量的双重需求。 一、思维融合&#xff1a;从质检岗到质量共建者 1.1 破除“上下游”对立意识 传统团队中&#xff0c;测试常被视作…

作者头像 李华