首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >std::lock_guard 深入解析

std::lock_guard 深入解析

原创
作者头像
码事漫谈
发布2025-06-30 13:48:04
发布2025-06-30 13:48:04
44100
代码可运行
举报
文章被收录于专栏:C++C++
运行总次数:0
代码可运行

在多线程编程中,确保线程安全是一个关键问题。std::lock_guard 是 C++ 标准库中提供的一种 RAII(Resource Acquisition Is Initialization)机制,用于自动管理互斥锁的加锁和解锁操作。本文将首先展示一个简化的 std::lock_guard 源码实现,然后详细解释其工作原理,并通过一个示例进一步说明如何使用 std::lock_guard

1. 简化的 std::lock_guard 源码实现

以下是 std::lock_guard 的简化源码实现:

代码语言:cpp
代码运行次数:0
运行
复制
template <typename Mutex>
class lock_guard {
public:
    using mutex_type = Mutex;

    // 普通构造函数,构造时加锁
    explicit lock_guard(Mutex& m) : m_(m) {
        m_.lock();
    }

    // 带adopt_lock参数的构造函数,构造时不加锁
    lock_guard(Mutex& m, std::adopt_lock_t) noexcept : m_(m) {}

    // 析构函数,析构时解锁
    ~lock_guard() noexcept {
        m_.unlock();
    }

    // 禁止拷贝构造和赋值操作
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;

private:
    Mutex& m_; // 保存对互斥锁的引用
};

2. 代码解释

2.1 构造函数

  • 普通构造函数:explicit lock_guard(Mutex& m) : m_(m) { m_.lock(); }
    • 接受一个互斥锁对象的引用 m,并将其保存在成员变量 m_ 中。
    • 在构造时调用互斥锁的 lock() 方法,对互斥锁进行加锁。
  • adopt_lock 参数的构造函数:lock_guard(Mutex& m, std::adopt_lock_t) noexcept : m_(m) {}
    • 同样接受一个互斥锁对象的引用 m,但不会在构造时加锁。
    • 这种构造方式通常用于当互斥锁已经被当前线程加锁,但需要 lock_guard 在作用域结束时自动解锁的情况。

2.2 析构函数

代码语言:cpp
代码运行次数:0
运行
复制
~lock_guard() noexcept {
    m_.unlock();
}
  • lock_guard 对象析构时,调用互斥锁的 unlock() 方法,释放互斥锁。

2.3 禁止拷贝构造和赋值操作

代码语言:cpp
代码运行次数:0
运行
复制
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
  • 通过将拷贝构造函数和赋值操作符函数声明为删除函数,禁止 lock_guard 对象的拷贝和赋值操作。
  • 这是因为互斥锁的管理是基于对象生命周期的,如果允许拷贝或赋值,可能会导致多个 lock_guard 对象同时管理同一个互斥锁,从而引发潜在的线程安全问题。

2.4 私有成员变量

代码语言:cpp
代码运行次数:0
运行
复制
Mutex& m_; // 保存对互斥锁的引用
  • m_ 是一个对互斥锁的引用,用于在构造和析构时操作互斥锁。

3. 示例说明

为了进一步说明如何使用 std::lock_guard,我们通过一个示例来展示其在多线程环境中的使用。假设我们有一个共享的资源 sharedResource,需要多个线程安全地对其进行访问和修改。

示例代码

代码语言:cpp
代码运行次数:0
运行
复制
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

std::mutex mtx;
int sharedResource = 0;

void threadFunction(int id) {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx); // 自动加锁
        sharedResource += id;
        // 当 lock 的作用域结束时,自动解锁
    }
}

int main() {
    std::vector<std::thread> threads;

    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(threadFunction, i);
    }

    for (auto& thread : threads) {
        thread.join();
    }

    std::cout << "sharedResource: " << sharedResource << std::endl;

    return 0;
}

示例解释

  1. 共享资源
    • sharedResource 是一个全局变量,多个线程将对其进行访问和修改。
  2. 线程函数
    • threadFunction 是线程执行的函数,每个线程会将 sharedResource 增加 id 的值,重复 10000 次。
    • 在每次修改 sharedResource 之前,使用 std::lock_guard 对互斥锁 mtx 进行加锁,确保同一时间只有一个线程可以访问 sharedResource
  3. 线程创建和同步
    • main 函数中,创建了 10 个线程,每个线程执行 threadFunction
    • 使用 std::thread::join 确保所有线程完成执行后,主线程继续运行。
  4. 输出结果
    • 最终,sharedResource 的值将被正确计算并输出。

示例输出

假设每个线程的 id 从 0 到 9,每个线程将 sharedResource 增加 id 的值 10000 次,最终 sharedResource 的值将是:

代码语言:txt
复制
sharedResource: 450000

4. 总结

std::lock_guard 是 C++ 标准库中提供的一种 RAII 机制,用于自动管理互斥锁的加锁和解锁操作。通过构造函数和析构函数的自动调用,std::lock_guard 确保互斥锁在作用域结束时自动释放,从而避免因忘记解锁而导致的死锁问题。合理使用 std::lock_guard 可以有效提高多线程程序的稳定性和可靠性。

通过本文的简化源码实现、代码解释和示例说明,希望读者能够更好地理解和应用 std::lock_guard。在实际编程中,合理使用同步机制可以有效避免数据竞争和未定义行为,提高程序的稳定性和可靠性。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 简化的 std::lock_guard 源码实现
  • 2. 代码解释
    • 2.1 构造函数
    • 2.2 析构函数
    • 2.3 禁止拷贝构造和赋值操作
    • 2.4 私有成员变量
  • 3. 示例说明
    • 示例代码
    • 示例解释
    • 示例输出
  • 4. 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档