C++中的锁
互斥锁
mutex(mutual exclusion)是C++中一种用于保证资源在某一时刻只有一个线程访问的同步原语(synchronization primitive)。这用于避免竞态条件(race conditions)并保证共享数据的线程安全访问。
一个最基本的使用互斥锁std::mutex
进行线程互斥访问的例子:
- 引入相关头文件
- 定义互斥锁,用于保障共享资源的互斥访问
std::mutex mtx;
int shared_counter = 0; // Shared data - 定义函数
incrementCounter
多次增加共享资源计数void incrementCounter(int times) {
for (int i = 0; i < times; i++) {
mtx.lock(); // Acquire the lock
shared_counter++; // Safely access the shared data
mtx.unlock(); // Release the lock
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Sleep for a while (optional)
}
}
- 创建多个线程并发修改共享资源计数结果输出为50,每个线程都正确的执行了共享资源的自增。如果去掉互斥锁,则结果可能会小于50。
int main() {
const int num_threads = 5;
const int increments_per_thread = 10;
std::thread threads[num_threads];
// Launch the threads
for (int i = 0; i < num_threads; i++) {
threads[i] = std::thread(incrementCounter, increments_per_thread);
}
// Join the threads
for (int i = 0; i < num_threads; i++) {
threads[i].join();
}
std::cout << "Final value of shared_counter: " << shared_counter << std::endl;
return 0;
}
上述第三步中mtx.lock()
和mtx.unlock()
构成了一段临界区(critical section),其间的操作是线程安全的。对于声明临界区一般选择避免直接操作mutex,而是使用基于RAII(Resource Acquisition Is Initialization)的写法:
std::unique_lock<std::mutex> lock(mtx)
用于基于持有一个std::mutex
对象创建一个lock
对象并在构造方法中调用mtx.lock()
来实现加锁。可以通过调用lock.unlock()
来显式调用mtx.unlock()
来释放锁,也可在lock
对象析构时自动调用mtx.unlock()
释放资源,从而可以让锁的作用域更加清晰并避免了编码人员忘记释放资源而造成死锁。所以应尽量避免直接调用std::mutex
对象的方法而推荐用std::unique_lock<std::mutex>
进行锁的申请与释放。例:void incrementCounter(int times) {
for (int i = 0; i < times; i++) {
std::unique_lock<std::mutex> lock(mtx); // Acquire the lock
shared_counter++; // Safely access the shared data
mtx.unlock(); // Release the lock
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Sleep for a while (optional)
}
}std::lock_guard<std::mutex> lock(mtx)
和std::unique_lock<std::mutex>
类似,差别是std::unique_lock<std::mutex>
更严格:不提供主动unlock()
的接口,只能在构造时加锁,析构时释放锁。例:void incrementCounter(int times) {
for (int i = 0; i < times; i++) {
std::lock_guard<std::mutex> lock(mtx); // Acquire the lock
shared_counter++; // Safely access the shared data
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // Sleep for a while (optional)
// Release the lock here automatically
}
}