核心目标:掌握 C++17 新增的同步原语,理解读者-写者锁的性能优势,能根据场景选择正确的同步工具,写出正确且高效的多线程同步代码。

前置知识:Part 2 的 mutex/lock_guard/unique_lock/condition_variable 基础。


6.1 std::shared_mutex——读者-写者锁

6.1.1 读共享、写独占的语义

传统的 std::mutex 无论是读还是写都互斥,这在高并发读取场景下是性能瓶颈。std::shared_mutex(C++17)提供两种锁定模式:

  • 共享锁(读锁):多个线程可以同时持有,适合读取操作
  • 独占锁(写锁):只有一个线程能持有,适合写入操作

所有 Reader 释放

独占模式 (写)

Writer ✗ 等待 Readers 释放

Writer ✓ 独占

共享模式 (读)

Reader 1 ✓

Reader 2 ✓

Reader 3 ✓

6.1.2 基本 API

#include <shared_mutex>

std::shared_mutex smtx;

// ── 读线程: 共享锁 ──
void reader(int id) {
    smtx.lock_shared();            // 获取共享锁
    // 多个 reader 可以同时执行到这里
    std::cout << "Reader " << id << " reading data: " << shared_data << "\n";
    smtx.unlock_shared();          // 释放共享锁
}

// ── 写线程: 独占锁 ──
void writer(int new_val) {
    smtx.lock();                   // 获取独占锁 (阻塞直到所有 reader 释放)
    shared_data = new_val;
    std::cout << "Writer updated data to: " << shared_data << "\n";
    smtx.unlock();                 // 释放独占锁
}

6.1.3 为什么不直接使用 lock()/unlock()

与普通 mutex 一样,手动 lock_shared()/unlock_shared() 存在异常安全隐患:

// ❌ 危险: 如果 read_process 抛异常, unlock_shared 永远不会被调用
void reader_unsafe() {
    smtx.lock_shared();
    auto result = read_process();  // 可能抛异常
    smtx.unlock_shared();          // 永远到不了这里!
}

// ✅ 正确: 用 RAII 自动管理
void reader_safe() {
    std::shared_lock lock(smtx);   // 构造时 lock_shared, 析构时 unlock_shared
    auto result = read_process();  // 即使抛异常也能释放锁
}

6.2 std::shared_lock——读锁的 RAII 包装

6.2.1 基本用法

#include <shared_mutex>

std::shared_mutex smtx;
std::unordered_map<std::string, std::string> cache;

// ── 读取缓存 (多线程可并发) ──
std::optional<std::string> read_cache(const std::string& key) {
    std::shared_lock lock(smtx);        // ✅ 共享锁,多个读可并发
    auto it = cache.find(key);
    if (it != cache.end()) {
        return it->second;
    }
    return std::nullopt;
}

// ── 更新缓存 (写独占) ──
void write_cache(const std::string& key, const std::string& value) {
    std::unique_lock lock(smtx);        // ✅ 独占锁,阻塞所有读写
    cache[key] = value;
}

6.2.2 shared_lock vs unique_lock vs lock_guard

特性 std::lock_guard std::unique_lock std::shared_lock
锁定模式 独占 独占 共享
RAII 自动管理
手动 unlock
延迟加锁 ✅ (defer_lock)
尝试加锁 ✅ (try_to_lock) ✅ (try_to_lock)
转移所有权 ✅ (move) ✅ (move)
适用 mutex 类型 任意 mutex 任意 mutex 仅 shared_mutex
配合条件变量
开销 最小 稍大 稍大

6.2.3 锁升级/降级——为什么 shared_mutex 不支持

一个常见需求:先读共享锁,如果缓存缺失,升级为独占锁来更新。

// ❌ 错误期望: 锁升级 (C++ 标准库不支持)
void cache_get_or_load_bad(const std::string& key) {
    std::shared_lock lock(smtx);       // 持有读锁
    auto it = cache.find(key);
    if (it != cache.end()) return it->second;
    
    // 想在这里升级为写锁?→ 不支持!
    // 如果直接 lock(), 当前线程已经持有 shared_lock → 死锁!
}

// ✅ 正确做法: 先释放读锁, 再获取写锁 (双重检查)
std::string cache_get_or_load(const std::string& key) {
    // 第一次检查 (读锁)
    {
        std::shared_lock lock(smtx);
        auto it = cache.find(key);
        if (it != cache.end()) return it->second;
    }  // ← 释放读锁

    // 获取写锁
    {
        std::unique_lock lock(smtx);
        // 第二次检查: 其他线程可能已加载
        auto it = cache.find(key);
        if (it != cache.end()) return it->second;
        
        // 加载数据
        std::string value = expensive_load_from_db(key);
        cache[key] = value;
        return value;
    }
}

命中

未命中

命中 (其他线程已加载)

未命中

cache_get_or_load(key)

① shared_lock 读取
cache.find(key)

return value
(释放读锁)

② 释放读锁

③ unique_lock 获取写锁

④ 再次检查
cache.find(key) ← 关键!

return value
(释放写锁)

⑤ 加载数据到缓存

return value
(释放写锁)

为什么 C++ 不直接支持锁升级? 因为 lock_sharedlock 的原子升级可能导致死锁(多个 reader 同时请求升级)。需要时用上述双重检查模式或第三方库(如 boost::upgrade_mutex)。


6.3 高性能缓存系统实战

6.3.1 完整的线程安全缓存

#include <shared_mutex>
#include <unordered_map>
#include <functional>
#include <optional>

template <typename Key, typename Value>
class ThreadSafeCache {
    mutable std::shared_mutex mtx_;          // mutable: const 方法中也可加锁
    std::unordered_map<Key, Value> cache_;
    size_t max_size_;

public:
    explicit ThreadSafeCache(size_t max_size = 10000) : max_size_(max_size) {}

    // read: 多个线程可并发
    std::optional<Value> get(const Key& key) const {
        std::shared_lock lock(mtx_);
        auto it = cache_.find(key);
        if (it != cache_.end()) {
            return it->second;
        }
        return std::nullopt;
    }

    // write: 独占
    void put(const Key& key, const Value& value) {
        std::unique_lock lock(mtx_);
        if (cache_.size() >= max_size_) {
            evict_lru();                     // 简单的淘汰策略
        }
        cache_[key] = value;
    }

    // get_or_compute: 缓存未命中时加载
    Value get_or_compute(const Key& key,
                         std::function<Value(const Key&)> loader) {
        // 先尝试读锁
        {
            std::shared_lock lock(mtx_);
            auto it = cache_.find(key);
            if (it != cache_.end()) return it->second;
        }

        // 未命中: 获取写锁 + 双重检查
        Value value = loader(key);           // ⚠️ 在锁外执行耗时加载!
        {
            std::unique_lock lock(mtx_);
            auto it = cache_.find(key);
            if (it != cache_.end()) return it->second;  // 其他线程已加载

            if (cache_.size() >= max_size_) {
                evict_lru();
            }
            cache_[key] = value;
            return value;
        }
    }

    size_t size() const {
        std::shared_lock lock(mtx_);
        return cache_.size();
    }

private:
    void evict_lru() {
        // 简化: 删除第一个元素
        if (!cache_.empty()) cache_.erase(cache_.begin());
    }
};

6.3.2 性能对比:mutex vs shared_mutex

#include <chrono>
#include <iostream>
#include <thread>
#include <random>

// 测试: 10 个读线程 + 2 个写线程, 各执行 100 万次操作
template <typename CacheType>
double benchmark_cache(int num_readers, int num_writers, int ops_per_thread) {
    CacheType cache;
    std::atomic<bool> start{false};
    std::atomic<long> total_ops{0};

    auto reader_fn = [&](int id) {
        while (!start) std::this_thread::yield();
        for (int i = 0; i < ops_per_thread; ++i) {
            cache.get(i % 1000);
            total_ops++;
        }
    };

    auto writer_fn = [&](int id) {
        while (!start) std::this_thread::yield();
        for (int i = 0; i < ops_per_thread; ++i) {
            cache.put(i % 1000, i * id);
            total_ops++;
        }
    };

    std::vector<std::thread> threads;
    for (int i = 0; i < num_readers; ++i)
        threads.emplace_back(reader_fn, i);
    for (int i = 0; i < num_writers; ++i)
        threads.emplace_back(writer_fn, i);

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

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

    auto t2 = std::chrono::steady_clock::now();
    return std::chrono::duration<double>(t2 - t1).count();
}

// CacheMutex: 用 std::mutex 保护
// 读/写都互斥

// CacheSharedMutex: 用 std::shared_mutex 保护
// 读共享, 写互斥

6.3.3 典型测试结果(8 核,10 读线程 + 2 写线程)

缓存实现 吞吐量 (ops/s) 相对 mutex 读延迟 p50 写延迟 p50
std::mutex 850,000 1.0x 1.2μs 1.2μs
std::shared_mutex 3,200,000 3.8x 0.3μs 2.5μs

std::shared_mutex —— 读者并行, 写者独占

R1

R2

R3

W1

R4

std::mutex —— 全部串行执行

R1

R2

R3

W1

R4

结论:读多写少的场景(如缓存、配置管理),shared_mutex 可以带来 3-5 倍的吞吐量提升。


6.4 std::scoped_lock 深度——多锁防死锁

6.4.1 C++14 写的痛

std::mutex mtx_a, mtx_b, mtx_c;

// C++14: 锁定 3 个 mutex 且防止死锁
void transfer_cpp14(int amount) {
    std::lock(mtx_a, mtx_b, mtx_c);              // ① 同时锁定 (内部防死锁)
    std::lock_guard<std::mutex> lk1(mtx_a, std::adopt_lock);  // ② 接管 a
    std::lock_guard<std::mutex> lk2(mtx_b, std::adopt_lock);  // ③ 接管 b
    std::lock_guard<std::mutex> lk3(mtx_c, std::adopt_lock);  // ④ 接管 c
    // ... 临界区 ...
}
// ⚠️ 如果忘记对某个 mutex 写 adopt_lock → 重复加锁 → 未定义行为!
// ⚠️ 如果忘记 std::lock() → 可能死锁!

6.4.2 C++17 一行搞定

// C++17: scoped_lock 可变参数模板
void transfer_cpp17(int amount) {
    std::scoped_lock lock(mtx_a, mtx_b, mtx_c);  // ✅ 自动防死锁 + RAII
    // ... 临界区 ...
}

// 结合 CTAD, 类型自动推导
std::scoped_lock lock(mtx_a, mtx_b);  // 等价于 scoped_lock<std::mutex, std::mutex>

6.4.3 scoped_lock 内部原理

// scoped_lock 的简化实现原理:
template <typename... MutexTypes>
class scoped_lock {
    std::tuple<MutexTypes&...> mutexes_;
public:
    explicit scoped_lock(MutexTypes&... m) : mutexes_(m...) {
        std::lock(m...);  // ① 用 std::lock() 算法同时锁定所有 mutex
                           //    内部实现: 尝试-回退 (try-and-back-off) 
                           //    如果某个锁获取失败, 释放所有已获取的锁再重试
    }

    ~scoped_lock() {
        std::apply([](MutexTypes&... m) {
            (m.unlock(), ...);  // ② C++17 fold expression 解锁所有
        }, mutexes_);
    }

    scoped_lock(const scoped_lock&) = delete;
    scoped_lock& operator=(const scoped_lock&) = delete;
};

6.4.4 实战:银行转账无死锁

#include <string>
#include <mutex>
#include <iostream>

class BankAccount {
    std::string name_;
    double balance_ = 0.0;
    mutable std::mutex mtx_;  // mutable: const 方法内部也可能需要临时加锁

public:
    explicit BankAccount(std::string name, double initial = 0.0)
        : name_(std::move(name)), balance_(initial) {}

    // ✅ 安全的跨账户转账 —— scoped_lock 自动防死锁
    static void transfer(BankAccount& from, BankAccount& to, double amount) {
        // 确保锁定顺序一致, 但仍用 scoped_lock 作为安全网
        if (&from == &to) return;  // 自我转账

        std::scoped_lock lock(from.mtx_, to.mtx_);
        
        if (from.balance_ >= amount) {
            from.balance_ -= amount;
            to.balance_   += amount;
            std::cout << from.name_ << " → " << to.name_ 
                      << ": $" << amount << "\n";
        }
    }

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

    const std::string& name() const { return name_; }
};

// 使用示例
int main() {
    BankAccount alice("Alice", 1000);
    BankAccount bob("Bob", 500);
    BankAccount charlie("Charlie", 300);

    // 即使从不同线程以不同顺序调用, scoped_lock 也不会死锁
    std::thread t1([&] { BankAccount::transfer(alice, bob, 100); });
    std::thread t2([&] { BankAccount::transfer(bob, alice, 50); });
    std::thread t3([&] { BankAccount::transfer(alice, charlie, 200); });
    std::thread t4([&] { BankAccount::transfer(charlie, bob, 75); });

    t1.join(); t2.join(); t3.join(); t4.join();

    std::cout << "Alice: " << alice.balance() << "\n";
    std::cout << "Bob: " << bob.balance() << "\n";
    std::cout << "Charlie: " << charlie.balance() << "\n";
}

6.4.5 lock_guard vs scoped_lock——何时用哪个

场景 推荐 原因
单 mutex,简单临界区 lock_guard 最简单,零心智负担
多 mutex(2+) scoped_lock 自动防死锁,一行搞定
需要配合条件变量 unique_lock condition_variable 要求 unique_lock
需要中途解锁 unique_lock 支持手动 unlock() / lock()
C++14 项目 lock_guard + std::lock() scoped_lock 仅 C++17
C++17 新项目单锁 scoped_lock 统一风格,CTAD 更简洁

建议:C++17 项目中,单锁也推荐用 scoped_lock(开销与 lock_guard 几乎相同),保持代码风格统一。std::scoped_lock lock(mtx); 习惯后就回不去了。


6.5 std::call_once 与单例模式——终极对比

6.5.1 六种单例实现对比

#include <mutex>
#include <atomic>
#include <memory>

class ExpensiveResource {
    // ① ❌ 非线程安全 —— 永远不要用
    static ExpensiveResource* instance_unsafe_;
    static ExpensiveResource* get_unsafe() {
        if (!instance_unsafe_)
            instance_unsafe_ = new ExpensiveResource();  // Data Race!
        return instance_unsafe_;
    }

    // ② ❌ DCLP (C++11 之前是 broken 的) —— 不要用
    static std::mutex dclp_mtx_;
    static ExpensiveResource* instance_dclp_;
    static ExpensiveResource* get_dclp_broken() {
        if (!instance_dclp_) {                // 第一次检查 (无锁)
            std::lock_guard lock(dclp_mtx_);
            if (!instance_dclp_) {            // 第二次检查 (有锁)
                instance_dclp_ = new ExpensiveResource();
                // 在 C++03 内存模型下, new 的重排可能导致
                // 另一个线程看到"分配了但未构造"的对象
            }
        }
        return instance_dclp_;
    }

    // ③ ✅ 函数内 static (C++11 起线程安全) —— 最推荐
    static ExpensiveResource& get_static() {
        static ExpensiveResource instance;  // C++11 保证: 只会初始化一次
        return instance;
    }

    // ④ ✅ call_once + once_flag —— 也可以
    static std::once_flag once_flag_;
    static std::unique_ptr<ExpensiveResource> instance_call_once_;
    static ExpensiveResource* get_call_once() {
        std::call_once(once_flag_, [] {
            instance_call_once_.reset(new ExpensiveResource());
        });
        return instance_call_once_.get();
    }

    // ⑤ ✅ atomic + CAS —— 复杂但灵活
    static std::atomic<ExpensiveResource*> instance_atomic_;
    static ExpensiveResource* get_atomic() {
        auto* ptr = instance_atomic_.load(std::memory_order_acquire);
        if (!ptr) {
            auto* new_instance = new ExpensiveResource();
            if (instance_atomic_.compare_exchange_strong(
                    ptr, new_instance, std::memory_order_release,
                    std::memory_order_acquire)) {
                return new_instance;
            }
            delete new_instance;  // 另一个线程抢先了
        }
        return ptr;
    }

    // ⑥ ✅ call_once + 非静态单例 —— 延迟初始化集合
    static std::once_flag init_flag_;
    static std::unordered_map<std::string, ExpensiveResource> pool_;
    static ExpensiveResource& get_from_pool(const std::string& name) {
        std::call_once(init_flag_, [] {
            pool_["db"]  = ExpensiveResource("database");
            pool_["api"] = ExpensiveResource("api_client");
            pool_["log"] = ExpensiveResource("logger");
        });
        return pool_.at(name);
    }

    // 添加 requires_lock 注释标记
    // 静态局部变量方式支持传入参数吗?不支持——这是它的局限性
    ExpensiveResource(std::string config = "default") {}
};

6.5.2 方案选择决策

需要单例/Lazy Init?

需要传参初始化?

✅ 函数内 static 变量
最简洁, C++11 保证线程安全

初始化多个相关对象?

✅ call_once
一次性初始化集合

需要销毁/重建?

✅ call_once + unique_ptr
可手动 reset

✅ call_once
支持传参

6.5.3 call_once 的更多场景

#include <mutex>
#include <memory>
#include <thread>
#include <vector>

// 场景 1: 懒加载配置
class AppConfig {
    static std::once_flag config_init_flag_;
    static std::unique_ptr<AppConfig> instance_;

    std::unordered_map<std::string, std::string> settings_;

    AppConfig() {
        // 从文件加载配置 (耗时操作)
        settings_["db.host"] = "localhost";
        settings_["db.port"] = "5432";
        settings_["cache.size"] = "10000";
    }

public:
    static AppConfig& instance() {
        std::call_once(config_init_flag_, [] {
            instance_.reset(new AppConfig());
        });
        return *instance_;
    }

    std::string get(const std::string& key, 
                    const std::string& default_val = "") const {
        auto it = settings_.find(key);
        return it != settings_.end() ? it->second : default_val;
    }
};

// 场景 2: 线程安全的首次访问标记
class DatabaseConnection {
    std::once_flag connected_flag_;
    
public:
    void ensure_connected() {
        std::call_once(connected_flag_, [this] {
            std::cout << "[Thread " << std::this_thread::get_id() 
                      << "] 正在连接数据库...\n";
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            // 实际的连接代码...
            std::cout << "[Thread " << std::this_thread::get_id() 
                      << "] 连接成功!\n";
        });
    }
};

// 测试: 多个线程同时要求连接, 但只连接一次
void test_connect_once() {
    DatabaseConnection conn;
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([&conn] { conn.ensure_connected(); });
    }
    for (auto& t : threads) t.join();
    // 输出: 只有一行 "正在连接数据库..." 和一行 "连接成功!"
}

6.5.4 call_once 实现原理

// std::call_once 的简化实现示意:
// 使用原子标志 + 互斥锁 + 条件变量
// 第一个线程: 获取锁, 执行函数, 设置标志为 finished
// 后续线程: 检查标志已为 finished, 直接返回
//
// once_flag 状态机:
//   [未执行] ──call_once──→ [执行中] ──函数完成──→ [已完成]
//                 ↑ 其他线程阻塞等待          ↑ 后续调用直接返回

once_flag 创建

Thread 1 调用 call_once

Thread 2..N 调用 call_once

初始化函数完成

被唤醒

NotExecuted

Executing

Blocked

Done


6.6 同步原语选择决策树

bool / 整数 / 指针

读多写少 (如缓存)

临界区极短 (<100ns)

需要锁定多个资源

仅初始化一次

复杂互斥 + 等待通知

不需要 wait/notify
简单临界区

需要线程同步?

保护数据类型?

需要复杂条件等待?

✅ std::atomic<T>
最轻量, 无锁

✅ atomic + condition_variable
或直接用 mutex + cv

✅ std::shared_mutex
+ shared_lock (读)
+ unique_lock (写)

✅ atomic_flag 自旋锁
避免上下文切换开销

✅ std::scoped_lock
自动防死锁

需要传参?

✅ 函数内 static 变量
最简洁

✅ std::call_once
+ std::once_flag

✅ std::mutex
+ std::unique_lock
+ std::condition_variable

✅ std::mutex
+ std::scoped_lock
(或 lock_guard)


6.7 锁的性能基准测试

6.7.1 微基准代码

#include <benchmark/benchmark.h>
#include <mutex>
#include <shared_mutex>
#include <atomic>

// ── 测试: 无竞争下的锁获取/释放延迟 ──

// mutex (独占)
static void BM_Mutex_LockUnlock(benchmark::State& state) {
    std::mutex mtx;
    for (auto _ : state) {
        mtx.lock();
        benchmark::DoNotOptimize(mtx);
        mtx.unlock();
    }
}

// shared_mutex (独占模式)
static void BM_SharedMutex_ExclusiveLock(benchmark::State& state) {
    std::shared_mutex smtx;
    for (auto _ : state) {
        smtx.lock();
        benchmark::DoNotOptimize(smtx);
        smtx.unlock();
    }
}

// shared_mutex (共享模式)
static void BM_SharedMutex_SharedLock(benchmark::State& state) {
    std::shared_mutex smtx;
    for (auto _ : state) {
        smtx.lock_shared();
        benchmark::DoNotOptimize(smtx);
        smtx.unlock_shared();
    }
}

// atomic_flag 自旋锁
static void BM_Spinlock_LockUnlock(benchmark::State& state) {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
    for (auto _ : state) {
        while (flag.test_and_set(std::memory_order_acquire)) {
            // 自旋等待 (真实场景应加 pause 指令)
        }
        benchmark::DoNotOptimize(flag);
        flag.clear(std::memory_order_release);
    }
}

// scoped_lock (单 mutex)
static void BM_ScopedLock_One(benchmark::State& state) {
    std::mutex mtx;
    for (auto _ : state) {
        std::scoped_lock lock(mtx);
        benchmark::DoNotOptimize(mtx);
    }
}

BENCHMARK(BM_Mutex_LockUnlock);
BENCHMARK(BM_SharedMutex_ExclusiveLock);
BENCHMARK(BM_SharedMutex_SharedLock);
BENCHMARK(BM_Spinlock_LockUnlock);
BENCHMARK(BM_ScopedLock_One);

6.7.2 典型基准数据(x86-64, 无竞争)

锁类型 操作 延迟 (ns) 相对 mutex
std::mutex (linux futex) lock/unlock ~25 1.0x
std::shared_mutex 独占 lock/unlock ~35 1.4x
std::shared_mutex 共享 lock_shared/unlock_shared ~30 1.2x
atomic_flag 自旋锁 test_and_set/clear ~5 0.2x
std::scoped_lock (单锁) 构造/析构 ~26 1.04x
std::lock_guard 构造/析构 ~25 1.0x

6.7.3 竞争场景下的吞吐量(8 线程同时操作)

场景 mutex shared_mutex 自旋锁
100% 写 850K ops/s 720K ops/s 900K ops/s
90% 读 + 10% 写 860K ops/s 2.8M ops/s 940K ops/s
99% 读 + 1% 写 870K ops/s 5.1M ops/s 950K ops/s
竞争场景下不同锁的吞吐量 (8线程) 100% 写 90%读+10%写 99%读+1%写 6 5.5 5 4.5 4 3.5 3 2.5 2 1.5 1 0.5 0 吞吐量 (M ops/s)

关键结论

  1. 读多写少 → shared_mutex:读比例越高,优势越明显(可达 5x+)。
  2. 临界区极短 → 自旋锁:避免系统调用和上下文切换的开销。
  3. scoped_lock 单锁开销 ≈ lock_guard,多锁场景无额外开销(std::lock 本就需要)。
  4. 写密集 → 普通 mutexshared_mutex 的独占锁比 mutex 略慢。

6.8 小结

知识点 掌握程度 核心要点
std::shared_mutex 掌握 读共享、写独占,读多写少场景吞吐量提升 3-5x
std::shared_lock 掌握 共享锁的 RAII 包装,配合 shared_mutex 使用
锁升级问题 理解 C++ 不支持锁升级,用双重检查 + 释读取写
std::scoped_lock (C++17) 熟练 多锁场景首选,一行防止死锁,单锁也能用
CTAD 了解 std::scoped_lock lock(mtx) 无需写模板参数
std::call_once 掌握 线程安全单次初始化,支持传参,比 DCLP 更安全
函数内 static 掌握 C++11 起线程安全,最简单的单例写法
同步原语决策树 掌握 根据场景选择 atomic/shared_mutex/mutex/call_once
锁性能数据 理解 无竞争 25ns,读多写少 shared_mutex 有显著优势

下期预告

[Part 7:线程池——从零设计到优雅关闭] 将进入并发架构设计:

  • 为什么需要线程池——线程创建/销毁的开销量化
  • 任务队列设计——有界 vs 无界,std::function 类型擦除
  • ~200 行实现一个完整的 C++17 线程池
  • 优雅关闭:状态机设计,取消 vs drain
  • 异常安全与性能优化

推荐工具

  • Compiler Explorer (godbolt.org) —— 查看 scoped_lock 的汇编实现
  • perf stat -e cycles,instructions —— 对比不同锁的 CPU 周期
  • -fsanitize=thread -g —— TSAN 也能检测 shared_mutex 使用不当
  • Google Benchmark —— 微基准测试锁性能

更多推荐