乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

mutex 与 lock_guard

定义与作用

互斥锁(mutex)提供线程间互斥访问,lock_guard 利用 RAII 自动管理加锁/解锁。

std::mutex mtx;
{
    std::lock_guard<std::mutex> lock(mtx);  // 构造时加锁
    // 临界区
}  // 析构时自动解锁

核心原理

互斥锁类型层次

死锁四条件

完整示例

示例一:飞翔科技线程安全计数器

场景说明:小崔实现一个线程安全的请求计数器供孔蓝的 API 网关使用。

#include <iostream>
#include <thread>
#include <mutex>
#include <vector>
#include <chrono>
#include <iomanip>

class RequestCounter {
public:
    void increment() {
        std::lock_guard<std::mutex> lock(mtx_);
        ++count_;
    }

    uint64_t get() const {
        std::lock_guard<std::mutex> lock(mtx_);
        return count_;
    }

    // 重置并返回旧值(需要原子性)
    uint64_t reset() {
        std::lock_guard<std::mutex> lock(mtx_);
        auto old = count_;
        count_ = 0;
        return old;
    }

private:
    mutable std::mutex mtx_;  // mutable 允许在 const 方法中加锁
    uint64_t count_ = 0;
};

int main() {
    std::cout << "=== 飞翔科技 API 请求计数器 ===\n\n";

    RequestCounter counter;

    auto worker = [&counter](int id, int iterations) {
        for (int i = 0; i < iterations; ++i) {
            counter.increment();
            // 模拟请求处理
            std::this_thread::sleep_for(std::chrono::microseconds(10));
        }
    };

    const int kThreads = 8;
    const int kIterPerThread = 10000;
    std::vector<std::thread> threads;

    auto start = std::chrono::steady_clock::now();

    for (int i = 0; i < kThreads; ++i)
        threads.emplace_back(worker, i, kIterPerThread);

    for (auto& t : threads) t.join();

    auto end = std::chrono::steady_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

    uint64_t total = counter.get();
    uint64_t expected = kThreads * kIterPerThread;

    std::cout << "线程数: " << kThreads << "\n";
    std::cout << "每线程请求: " << kIterPerThread << "\n";
    std::cout << "期望总数: " << expected << "\n";
    std::cout << "实际计数: " << total << "\n";
    std::cout << "数据一致性: " << (total == expected ? "✓ 正确" : "✗ 错误") << "\n";
    std::cout << "总耗时: " << ms << "ms\n";
    std::cout << "吞吐量: " << (total * 1000.0 / ms) << " req/s\n";

    counter.reset();
    std::cout << "重置后计数: " << counter.get() << "\n";
}

预期输出:

=== 飞翔科技 API 请求计数器 ===

线程数: 8
每线程请求: 10000
期望总数: 80000
实际计数: 80000
数据一致性: ✓ 正确
总耗时: XXXms
吞吐量: XXXXX req/s
重置后计数: 0

逐段分析:

  • lock_guard 在构造时 mtx_.lock(),析构时 mtx_.unlock()
  • mutable 修饰 mtx_ 使 const 方法也可加锁
  • 每个 increment() 调用都是线程安全的
  • 80000 次操作零竞争错误

示例二:std::lock 同时加多锁防死锁

场景说明:白歌实现飞翔科技的双向转账,展示 std::lock 同时锁定两个账户。

#include <iostream>
#include <thread>
#include <mutex>
#include <string>
#include <vector>

class Account {
public:
    Account(std::string name, double balance)
        : name_(std::move(name)), balance_(balance) {}

    double balance() const {
        std::lock_guard<std::mutex> lock(mtx_);
        return balance_;
    }

    // 线程安全转账(使用 std::lock 同时锁定两个账户)
    static void transfer(Account& from, Account& to, double amount) {
        if (&from == &to) return;  // 同一账户无需加锁

        // std::lock 同时锁定两个 mutex,避免死锁
        std::lock(from.mtx_, to.mtx_);

        // 已加锁,使用 adopt_lock 告诉 lock_guard 不要再加锁
        std::lock_guard<std::mutex> lockFrom(from.mtx_, std::adopt_lock);
        std::lock_guard<std::mutex> lockTo(to.mtx_, std::adopt_lock);

        if (from.balance_ >= amount) {
            from.balance_ -= amount;
            to.balance_   += amount;
            std::cout << "  转账: " << from.name_ << " → " << to.name_
                      << " ¥" << amount << "\n";
        } else {
            std::cout << "  转账失败: " << from.name_ << " 余额不足\n";
        }
    }

private:
    std::string name_;
    double balance_;
    mutable std::mutex mtx_;
};

int main() {
    std::cout << "=== 飞翔科技内部转账系统 ===\n\n";

    Account a("大翔-工资卡",   100000.00);
    Account b("白歌-储蓄卡",    50000.00);
    Account c("小崔-零花钱",    10000.00);

    std::cout << "初始余额:\n";
    std::cout << "  " << "大翔: ¥" << a.balance() << "\n";
    std::cout << "  " << "白歌: ¥" << b.balance() << "\n";
    std::cout << "  " << "小崔: ¥" << c.balance() << "\n\n";

    // 模拟并发转账
    std::vector<std::thread> threads;

    // 可能产生死锁的并发转账
    threads.emplace_back([&]() {
        for (int i = 0; i < 5; ++i) {
            Account::transfer(a, b, 1000.0);  // a → b
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    });

    threads.emplace_back([&]() {
        for (int i = 0; i < 5; ++i) {
            Account::transfer(b, a, 500.0);   // b → a(反向!)
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }
    });

    threads.emplace_back([&]() {
        for (int i = 0; i < 3; ++i) {
            Account::transfer(c, a, 2000.0);  // c → a
            std::this_thread::sleep_for(std::chrono::milliseconds(2));
        }
    });

    for (auto& t : threads) t.join();

    std::cout << "\n最终余额:\n";
    std::cout << "  " << "大翔: ¥" << a.balance() << "\n";
    std::cout << "  " << "白歌: ¥" << b.balance() << "\n";
    std::cout << "  " << "小崔: ¥" << c.balance() << "\n";

    double total = a.balance() + b.balance() + c.balance();
    std::cout << "总额不变性: ¥" << total << " (初始: ¥160000.00) "
              << (total == 160000.00 ? "✓" : "✗") << "\n";
}

预期输出:

=== 飞翔科技内部转账系统 ===

初始余额:
  大翔: ¥100000
  白歌: ¥50000
  小崔: ¥10000

  转账: 大翔-工资卡 → 白歌-储蓄卡 ¥1000
  转账: 白歌-储蓄卡 → 大翔-工资卡 ¥500
  ...

最终余额:
  大翔: ¥XXX
  白歌: ¥XXX
  小崔: ¥XXX
总额不变性: ¥160000.00 (初始: ¥160000.00) ✓

逐段分析:

  • std::lock(m1, m2) 使用死锁避免算法同时锁定两个 mutex
  • std::adopt_lock 告诉 lock_guard 互斥锁已锁定,无需再加锁
  • 两个线程以相反顺序转账(a→b 和 b→a),若用两行独立 lock() 会死锁
  • 总额不变性验证:转账不会创造或毁灭资金

易错场景与面试考点

易错场景

1. 忘记 unlock 或 lock_guard 作用域错误

void bad() {
    mtx.lock();
    if (error) return;  // ❌ 提前返回未解锁
    mtx.unlock();
}
// ✅ 使用 lock_guard,自动管理

2. 双锁死锁

// ❌ 如果两个线程同时分别锁住 a 和 b
thread1: a.lock(); b.lock();  // 持有 a,等待 b
thread2: b.lock(); a.lock();  // 持有 b,等待 a → 死锁!

// ✅ 使用 std::lock(a, b) 或统一加锁顺序

3. recursive_mutex 过度使用

// recursive_mutex 可能是设计缺陷的信号
// 通常意味着类的职责不清晰,应该重构

面试考点

考点要点
lock_guard最简单 RAII,不可手动解锁,不可转移
unique_lock灵活 RAII,可延迟/提前/转移
scoped_lock(C++17)可变参数,同时锁多个 mutex
死锁四条件互斥、持有等待、不可剥夺、循环等待
std::lock避免死锁的同时加多锁
std::adopt_lock标记已锁定,lock_guard 不再加锁
std::defer_lock标记延后加锁,后续手动或 std::lock
std::try_to_lock尝试加锁,失败不阻塞
recursive_mutex同一线程可多次 lock,需等量 unlock
上一页
thread 基础与线程管理
下一页
unique_lock 与 condition_variable