跳转到内容

C++ 锁定策略

来自代码酷


概述[编辑 | 编辑源代码]

C++锁定策略是多线程编程中用于管理共享资源访问的核心机制,旨在防止数据竞争(Data Race)并确保线程安全。C++标准库通过std::mutexstd::lock_guardstd::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);  
    // 可重入代码  
}

2. 读写锁(std::shared_mutex)[编辑 | 编辑源代码]

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::lockstd::scoped_lock(C++17)同时锁定多个互斥量:

  
std::mutex mtx1, mtx2;  

void safe_transaction() {  
    std::scoped_lock lock(mtx1, mtx2); // 自动避免死锁  
    // 操作多个受保护资源  
}

性能考量[编辑 | 编辑源代码]

锁定策略的选择直接影响性能:

  • 粗粒度锁:简单但并发性低。
  • 细粒度锁:复杂但并发性高。

graph LR A[锁定策略] --> B[互斥锁] A --> C[读写锁] A --> D[无锁编程] B --> E[std::mutex] B --> F[std::recursive_mutex] C --> G[std::shared_mutex]

实际案例[编辑 | 编辑源代码]

场景:线程安全的日志系统

  
#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;  
    }  
};

数学基础[编辑 | 编辑源代码]

锁的公平性可通过排队理论分析。设锁争用概率为p,则平均等待时间: Twait=p1p×Tcritical

总结[编辑 | 编辑源代码]

  • 优先使用RAII锁(如std::lock_guard)。
  • 高并发读场景选择std::shared_mutex
  • 避免嵌套锁或使用递归锁。
  • 多锁操作时使用std::scoped_lock