C++ 锁定策略
外观
概述[编辑 | 编辑源代码]
C++锁定策略是多线程编程中用于管理共享资源访问的核心机制,旨在防止数据竞争(Data Race)并确保线程安全。C++标准库通过std::mutex
、std::lock_guard
、std::unique_lock
等工具提供多种锁定策略,开发者需根据场景选择适当的策略以平衡性能与安全性。
为什么需要锁定策略?[编辑 | 编辑源代码]
多线程环境下,若多个线程同时修改共享数据,可能导致未定义行为。例如:
#include <iostream>
#include <thread>
int counter = 0;
void increment() {
for (int i = 0; i < 100000; ++i) {
++counter; // 数据竞争!
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Counter: " << counter << std::endl; // 结果可能小于200000
}
输出示例(错误情况):
Counter: 132841
通过锁定策略可解决此问题。
基础锁定策略[编辑 | 编辑源代码]
1. 互斥锁(std::mutex)[编辑 | 编辑源代码]
最简单的锁定策略,通过lock()
和unlock()
显式控制临界区:
#include <mutex>
std::mutex mtx;
void safe_increment() {
for (int i = 0; i < 100000; ++i) {
mtx.lock();
++counter;
mtx.unlock();
}
}
注意:必须确保unlock()
被调用,否则会导致死锁。
2. 自动锁管理(RAII)[编辑 | 编辑源代码]
C++推荐使用RAII(Resource Acquisition Is Initialization)模式自动管理锁生命周期:
std::lock_guard[编辑 | 编辑源代码]
void safe_increment() {
for (int i = 0; i < 100000; ++i) {
std::lock_guard<std::mutex> lock(mtx); // 构造时加锁,析构时解锁
++counter;
}
}
std::unique_lock[编辑 | 编辑源代码]
更灵活的RAII锁,支持延迟锁定和所有权转移:
void transfer_lock(std::unique_lock<std::mutex> lock) {
// 锁所有权转移
}
void flexible_increment() {
std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟锁定
lock.lock();
++counter;
transfer_lock(std::move(lock)); // 转移所有权
}
高级锁定策略[编辑 | 编辑源代码]
1. 递归锁(std::recursive_mutex)[编辑 | 编辑源代码]
允许同一线程多次获取锁,解决嵌套调用问题:
std::recursive_mutex rec_mtx;
void nested_function() {
std::lock_guard<std::recursive_mutex> lock(rec_mtx);
// 可重入代码
}
[编辑 | 编辑源代码]
C++17引入,区分读写操作以提高并发性:
#include <shared_mutex>
std::shared_mutex sh_mtx;
void read_data() {
std::shared_lock<std::shared_mutex> lock(sh_mtx); // 共享锁(多读)
// 读取数据
}
void write_data() {
std::unique_lock<std::shared_mutex> lock(sh_mtx); // 独占锁(单写)
// 修改数据
}
3. 死锁避免策略[编辑 | 编辑源代码]
使用std::lock
或std::scoped_lock
(C++17)同时锁定多个互斥量:
std::mutex mtx1, mtx2;
void safe_transaction() {
std::scoped_lock lock(mtx1, mtx2); // 自动避免死锁
// 操作多个受保护资源
}
性能考量[编辑 | 编辑源代码]
锁定策略的选择直接影响性能:
- 粗粒度锁:简单但并发性低。
- 细粒度锁:复杂但并发性高。
实际案例[编辑 | 编辑源代码]
场景:线程安全的日志系统
#include <fstream>
#include <queue>
#include <shared_mutex>
class ThreadSafeLogger {
std::ofstream log_file;
std::shared_mutex file_mtx;
public:
void log(const std::string& message) {
std::unique_lock lock(file_mtx);
log_file << message << std::endl;
}
};
数学基础[编辑 | 编辑源代码]
锁的公平性可通过排队理论分析。设锁争用概率为,则平均等待时间:
总结[编辑 | 编辑源代码]
- 优先使用RAII锁(如
std::lock_guard
)。 - 高并发读场景选择
std::shared_mutex
。 - 避免嵌套锁或使用递归锁。
- 多锁操作时使用
std::scoped_lock
。