当前位置: 首页 > news >正文

C++ 锁类型大全详解

1. 锁的分类概览

C++ 中的锁可以分为几大类,让我用一个清晰的分类图来展示:

text

C++ 锁类型
├── 基本互斥锁 (Mutexes)
│   ├── std::mutex
│   ├── std::recursive_mutex
│   ├── std::timed_mutex
│   └── std::recursive_timed_mutex
├── 读写锁 (Read-Write Locks)
│   └── std::shared_mutex (C++17)
├── 锁管理器 (Lock Guards)
│   ├── std::lock_guard
│   ├── std::unique_lock
│   ├── std::shared_lock (C++14)
│   └── std::scoped_lock (C++17)
├── 无锁编程 (Lock-Free)
│   ├── std::atomic
│   └── std::atomic_flag
└── 其他同步原语├── std::condition_variable├── std::once_flag└── std::latch / std::barrier (C++20)

2. 基本互斥锁 (Mutexes)

2.1 std::mutex - 标准互斥锁

特点:最基本的互斥锁,不支持递归锁定

cpp

#include <mutex>
#include <thread>
#include <iostream>std::mutex mtx;
int shared_data = 0;void basic_mutex_example() {// 方法1:手动锁定(不推荐)mtx.lock();shared_data++;mtx.unlock();// 方法2:使用锁管理器(推荐)std::lock_guard<std::mutex> lock(mtx);shared_data++;
}void problem_example() {// 错误:同一线程重复锁定会导致死锁mtx.lock();mtx.lock();  // 这里会死锁!mtx.unlock();mtx.unlock();
}

2.2 std::recursive_mutex - 递归互斥锁

特点:允许同一线程多次锁定同一个互斥量

cpp

#include <mutex>
#include <iostream>std::recursive_mutex rec_mtx;void recursive_function(int depth) {std::lock_guard<std::recursive_mutex> lock(rec_mtx);std::cout << "深度: " << depth << std::endl;if (depth > 0) {recursive_function(depth - 1);  // 递归调用,需要递归锁}
}class RecursiveClass {
private:std::recursive_mutex mtx_;int data_ = 0;public:void method1() {std::lock_guard<std::recursive_mutex> lock(mtx_);data_++;method2();  // 调用另一个需要锁的方法}void method2() {std::lock_guard<std::recursive_mutex> lock(mtx_);  // 可以再次锁定data_ *= 2;}
};

2.3 std::timed_mutex - 定时互斥锁

特点:支持带超时的锁定操作

cpp

#include <mutex>
#include <thread>
#include <chrono>std::timed_mutex timed_mtx;void timed_example() {// 尝试立即锁定if (timed_mtx.try_lock()) {std::cout << "立即锁定成功" << std::endl;timed_mtx.unlock();}// 尝试在指定时间内锁定if (timed_mtx.try_lock_for(std::chrono::milliseconds(100))) {std::cout << "100ms内锁定成功" << std::endl;timed_mtx.unlock();} else {std::cout << "100ms内锁定失败" << std::endl;}// 尝试在指定时间点前锁定auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(50);if (timed_mtx.try_lock_until(deadline)) {std::cout << "在截止时间前锁定成功" << std::endl;timed_mtx.unlock();}
}// 使用 unique_lock 的定时功能
void timed_with_guard() {std::unique_lock<std::timed_mutex> lock(timed_mtx, std::defer_lock);if (lock.try_lock_for(std::chrono::milliseconds(50))) {std::cout << "使用锁管理器定时锁定成功" << std::endl;}
}

2.4 std::recursive_timed_mutex - 递归定时互斥锁

特点:结合了递归锁和定时锁的功能

cpp

#include <mutex>std::recursive_timed_mutex rec_timed_mtx;void recursive_timed_example() {// 可以递归锁定rec_timed_mtx.lock();rec_timed_mtx.lock();  // 允许,因为是递归锁// 也可以定时锁定if (rec_timed_mtx.try_lock_for(std::chrono::milliseconds(100))) {// 成功锁定rec_timed_mtx.unlock();}rec_timed_mtx.unlock();rec_timed_mtx.unlock();
}

3. 读写锁 (Read-Write Locks)

3.1 std::shared_mutex - 共享互斥锁 (C++17)

特点:允许多个读操作同时进行,但写操作需要独占访问

cpp

#include <shared_mutex>
#include <map>
#include <vector>
#include <thread>class ThreadSafeDictionary {
private:mutable std::shared_mutex rw_mutex_;std::map<std::string, std::string> data_;public:// 读操作 - 使用共享锁,允许多个线程同时读std::string lookup(const std::string& key) const {std::shared_lock<std::shared_mutex> lock(rw_mutex_);auto it = data_.find(key);if (it != data_.end()) {return it->second;}return "";}// 另一个读操作bool contains(const std::string& key) const {std::shared_lock<std::shared_mutex> lock(rw_mutex_);return data_.find(key) != data_.end();}// 写操作 - 使用独占锁,只允许一个线程写void insert(const std::string& key, const std::string& value) {std::unique_lock<std::shared_mutex> lock(rw_mutex_);data_[key] = value;}// 批量读操作std::vector<std::string> get_all_keys() const {std::shared_lock<std::shared_mutex> lock(rw_mutex_);std::vector<std::string> keys;for (const auto& pair : data_) {keys.push_back(pair.first);}return keys;}
};// 使用示例
void usage_example() {ThreadSafeDictionary dict;// 多个线程可以同时读取std::thread reader1([&]() {auto value = dict.lookup("key1");});std::thread reader2([&]() {auto keys = dict.get_all_keys();});// 但写操作会互斥std::thread writer([&]() {dict.insert("key1", "value1");});reader1.join();reader2.join();writer.join();
}

4. 锁管理器 (Lock Guards)

4.1 std::lock_guard - 基础锁管理器

特点:最简单的 RAII 锁管理器

cpp

#include <mutex>std::mutex mtx;void lock_guard_examples() {// 基本用法{std::lock_guard<std::mutex> lock(mtx);// 临界区代码} // 自动解锁// 保护整个函数void critical_function() {std::lock_guard<std::mutex> lock(mtx);// 整个函数都在锁保护下}// 保护部分代码块void partial_protection() {// 非临界区代码prepare_data();{std::lock_guard<std::mutex> lock(mtx);// 临界区代码process_shared_data();} // 锁在这里释放// 更多的非临界区代码cleanup();}
}

4.2 std::unique_lock - 灵活锁管理器

特点:提供完整的锁控制功能

cpp

#include <mutex>
#include <condition_variable>std::mutex mtx;
std::condition_variable cv;void unique_lock_examples() {// 1. 延迟锁定std::unique_lock<std::mutex> lock1(mtx, std::defer_lock);// ... 准备工作lock1.lock();  // 手动锁定// 2. 尝试锁定std::unique_lock<std::mutex> lock2(mtx, std::try_to_lock);if (lock2.owns_lock()) {// 锁定成功}// 3. 条件变量必须使用 unique_lockstd::unique_lock<std::mutex> lock3(mtx);cv.wait(lock3, []{ return some_condition; });// 4. 手动解锁和重新锁定std::unique_lock<std::mutex> lock4(mtx);// 临界区操作lock4.unlock();  // 手动解锁// 非临界区操作lock4.lock();    // 重新锁定// 回到临界区// 5. 移动语义std::unique_lock<std::mutex> lock5(mtx);// std::unique_lock<std::mutex> lock6 = lock5;  // 错误:不能拷贝std::unique_lock<std::mutex> lock6 = std::move(lock5);  // 正确:移动
}

4.3 std::shared_lock - 共享锁管理器 (C++14)

特点:专门用于管理共享锁(读锁)

cpp

#include <shared_mutex>std::shared_mutex rw_mutex;void shared_lock_examples() {// 读操作使用共享锁std::shared_lock<std::shared_mutex> read_lock(rw_mutex);// 多个线程可以同时持有共享锁// 支持所有 unique_lock 的功能std::shared_lock<std::shared_mutex> deferred_lock(rw_mutex, std::defer_lock);if (deferred_lock.try_lock()) {// 锁定成功}// 可以手动解锁read_lock.unlock();// 做一些不需要锁的操作read_lock.lock();  // 重新锁定
}

4.4 std::scoped_lock - 多锁管理器 (C++17)

特点:可以同时安全地锁定多个互斥量

cpp

#include <mutex>std::mutex mtx1, mtx2, mtx3;void scoped_lock_examples() {// 锁定单个互斥量(类似 lock_guard){std::scoped_lock lock(mtx1);// 临界区}// 锁定多个互斥量(避免死锁){std::scoped_lock lock(mtx1, mtx2, mtx3);// 所有互斥量都被安全锁定// 操作多个共享资源} // 所有锁自动释放// 对比:C++17 之前的做法{std::lock(mtx1, mtx2, mtx3);  // 同时锁定,避免死锁std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);std::lock_guard<std::mutex> lock3(mtx3, std::adopt_lock);// 更繁琐!}
}

5. 无锁编程 (Lock-Free)

5.1 std::atomic - 原子变量

特点:无需锁的线程安全操作

cpp

#include <atomic>
#include <thread>
#include <vector>void atomic_examples() {// 基本原子类型std::atomic<int> atomic_int(0);std::atomic<bool> atomic_bool(false);std::atomic<long> atomic_long(0L);// 原子操作atomic_int.fetch_add(1, std::memory_order_relaxed);atomic_bool.store(true, std::memory_order_release);// 运算符重载atomic_int++;  // 等价于 fetch_add(1)atomic_int += 5;// 比较交换 (Compare-And-Swap)int expected = 10;while (!atomic_int.compare_exchange_weak(expected, 20)) {// 如果 atomic_int != expected,则 expected 被更新为当前值// 继续重试}
}// 无锁计数器示例
class LockFreeCounter {
private:std::atomic<int> count_{0};public:void increment() {count_.fetch_add(1, std::memory_order_relaxed);}void decrement() {count_.fetch_sub(1, std::memory_order_relaxed);}int get() const {return count_.load(std::memory_order_acquire);}// 无锁的获取并重置int exchange(int new_value) {return count_.exchange(new_value, std::memory_order_acq_rel);}
};

5.2 std::atomic_flag - 原子标志

特点:最简单的原子类型,保证无锁

cpp

#include <atomic>
#include <thread>void atomic_flag_examples() {// 初始化(必须使用 ATOMIC_FLAG_INIT)std::atomic_flag flag = ATOMIC_FLAG_INIT;// 测试并设置(原子操作)bool was_set = flag.test_and_set(std::memory_order_acquire);// 清除标志flag.clear(std::memory_order_release);// 检查状态(C++20)// bool is_set = flag.test(std::memory_order_acquire);  // C++20
}// 自旋锁实现
class SpinLock {
private:std::atomic_flag flag_ = ATOMIC_FLAG_INIT;public:void lock() {while (flag_.test_and_set(std::memory_order_acquire)) {// 忙等待,但可以让出CPUstd::this_thread::yield();}}void unlock() {flag_.clear(std::memory_order_release);}
};// 使用自旋锁
SpinLock spin_lock;
void use_spinlock() {std::lock_guard<SpinLock> lock(spin_lock);  // 锁管理器也可以管理自旋锁!// 临界区
}

6. 其他同步原语

6.1 std::condition_variable - 条件变量

特点:用于线程间的条件同步

cpp

#include <condition_variable>
#include <queue>class ThreadSafeQueue {
private:std::mutex mtx_;std::condition_variable cv_;std::queue<int> queue_;bool stopped_ = false;public:void push(int value) {{std::lock_guard<std::mutex> lock(mtx_);queue_.push(value);}cv_.notify_one();  // 通知一个等待的消费者}bool pop(int& value) {std::unique_lock<std::mutex> lock(mtx_);// 等待条件:队列不为空或已停止cv_.wait(lock, [this]() {return !queue_.empty() || stopped_;});if (queue_.empty() && stopped_) {return false;  // 队列已空且已停止}value = queue_.front();queue_.pop();return true;}void stop() {{std::lock_guard<std::mutex> lock(mtx_);stopped_ = true;}cv_.notify_all();  // 通知所有等待的线程}
};

6.2 std::once_flag - 一次性初始化

特点:确保某个操作只执行一次

cpp

#include <mutex>class Singleton {
private:static std::once_flag init_flag_;static Singleton* instance_;Singleton() = default;public:static Singleton& get_instance() {std::call_once(init_flag_, []() {instance_ = new Singleton();});return *instance_;}
};std::once_flag Singleton::init_flag_;
Singleton* Singleton::instance_ = nullptr;// 使用示例
void use_singleton() {auto& instance1 = Singleton::get_instance();  // 第一次调用会创建实例auto& instance2 = Singleton::get_instance();  // 后续调用直接返回实例// instance1 和 instance2 是同一个对象
}

6.3 std::latch 和 std::barrier (C++20)

特点:用于线程汇聚点同步

cpp

#include <latch>
#include <barrier>
#include <thread>
#include <vector>// std::latch - 一次性屏障
void latch_example() {const int num_threads = 5;std::latch latch(num_threads);  // 需要5个线程到达std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&latch, i]() {// 每个线程做一些工作std::this_thread::sleep_for(std::chrono::milliseconds(i * 100));std::cout << "线程 " << i << " 完成工作" << std::endl;latch.count_down();  // 减少计数latch.wait();        // 等待所有线程完成std::cout << "所有线程都完成了!" << std::endl;});}for (auto& t : threads) {t.join();}
}// std::barrier - 可重复使用的屏障
void barrier_example() {const int num_threads = 3;std::barrier barrier(num_threads);  // 需要3个线程到达std::vector<std::thread> threads;for (int i = 0; i < num_threads; ++i) {threads.emplace_back([&barrier, i]() {for (int phase = 0; phase < 3; ++phase) {std::cout << "线程 " << i << " 阶段 " << phase << std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));barrier.arrive_and_wait();  // 到达并等待其他线程std::cout << "阶段 " << phase << " 所有线程同步完成" << std::endl;}});}for (auto& t : threads) {t.join();}
}

7. 锁的选择指南

7.1 性能对比表

锁类型适用场景性能功能
std::mutex一般用途中等基本
std::recursive_mutex递归调用稍慢递归锁定
std::timed_mutex需要超时稍慢定时操作
std::shared_mutex读多写少读快写慢读写分离
std::atomic简单操作最快无锁
SpinLock短期锁定很快忙等待

7.2 选择策略

cpp

// 策略1:简单临界区
void simple_case() {std::mutex mtx;std::lock_guard<std::mutex> lock(mtx);  // 性能最好// 操作...
}// 策略2:需要条件变量
void condition_case() {std::mutex mtx;std::condition_variable cv;std::unique_lock<std::mutex> lock(mtx);  // 必须用 unique_lockcv.wait(lock, []{ return condition; });
}// 策略3:读多写少
void read_heavy_case() {std::shared_mutex rw_mtx;// 读操作(多个线程可以同时读)std::shared_lock<std::shared_mutex> read_lock(rw_mtx);// 写操作(独占访问)std::unique_lock<std::shared_mutex> write_lock(rw_mtx);
}// 策略4:需要锁定多个资源
void multiple_resources() {std::mutex mtx1, mtx2;std::scoped_lock lock(mtx1, mtx2);  // 避免死锁
}// 策略5:高性能简单操作
void high_performance() {std::atomic<int> counter(0);counter.fetch_add(1, std::memory_order_relaxed);  // 无锁,最快
}

8. 总结

C++ 提供了丰富的锁类型来满足不同的并发需求:

  1. 基本互斥锁:保护共享数据的基本工具

  2. 读写锁:优化读多写少的场景

  3. 锁管理器:基于 RAII 的安全锁管理

  4. 无锁编程:最高性能的原子操作

  5. 同步原语:复杂的线程协调工具

关键建议

  • 优先使用锁管理器,避免手动锁操作

  • 根据场景选择合适的锁类型

  • 保持临界区尽可能小

  • 读写分离场景使用读写锁

  • 简单操作用原子变量替代锁

掌握这些锁类型,你就能应对各种多线程编程场景了!

http://www.dtcms.com/a/569187.html

相关文章:

  • 智慧园区:智能管理赋能未来发展新生态
  • 潮州 网站建设个人静态网站首页怎么做
  • 东莞网站建站推广wordpress导入演示数据
  • socket_udp
  • 基于单片机的智能家居窗帘控制系统设计(论文+源码)
  • Nestjs框架: 微服务架构拆分原则与实战指南
  • WinSCP的简单使用与SFTP自动备份 .bat脚本
  • iOS 虚拟位置设置实战,多工具协同打造精准调试与场景模拟环境
  • Qt 全球峰会 2025:中国站速递 —— 技术中立,拥抱更大生态
  • Android集成Unity避坑指南
  • 我的网站设计联盟网站推广营销应该怎么做
  • 从零开始刷算法-栈-括号匹配
  • 走进Linux的世界:初识进程(Task)
  • 首钢建设集团山东公司网站2017年网站建设公司
  • 让数据库更智能-大模型如何优化我们的SQL查询
  • 什么程序做网站容易优化apache和wordpress
  • NLP自然语言处理Bert大模型系列学习
  • 数据科学每日总结--Day10--数据库
  • 【实战】自然语言处理--长文本分类(3)HAN算法
  • 中国建设工程招投网站网站后台登陆口
  • 学校网站建设招聘电商推广计划
  • Ubuntu 20.04 系统库管理详细教程
  • [jmeter-商城测试]
  • Kubernetes包管理利器:Helm核心功能与架构解析指南
  • 17、docker-macvlan-1-理论
  • Mac M系列芯片制作Oracle19镜像使用docker-compose运行
  • Linux source命令详解与应用场景
  • Verilog学习 有限状态机
  • 企业网站备案审核需要多长时间沧州大型企业网站建设
  • Figma高效开发工具链:从设计到测试的完整解决方案