C++ 高级编程:1. 多线程基本操作

C++ 线程库完全指南:从基础到实战

前言

在多核处理器普及的今天,多线程编程已经成为提升程序性能的必备技能。C++11 标准终于将线程支持纳入了标准库,结束了长期以来依赖平台特定API(如Windows的CreateThread、Linux的pthread)的历史。

标准线程库(<thread>)提供了跨平台的线程创建、管理和同步机制,配合<mutex><condition_variable><future>等头文件,构成了完整的并发编程基础。本文将从最基础的概念开始,带你全面掌握C++线程库的使用。

C++ 线程库主要包含在 <thread><mutex><condition_variable><future> 等头文件中。以下是核心函数和类的总结:

线程库函数列表

1. 线程管理 (<thread>)

函数/类 说明
std::thread 线程类
thread::join() 等待线程完成
thread::detach() 分离线程(后台运行)
thread::joinable() 检查是否可 join
std::this_thread::get_id() 获取当前线程 ID
std::this_thread::sleep_for(duration) 当前线程睡眠一段时间
std::this_thread::sleep_until(time_point) 睡眠到指定时间点
std::this_thread::yield() 让出 CPU 时间片
std::hardware_concurrency() 获取硬件并发线程数
cpp 复制代码
#include <thread>
std::thread t([](){ /* work */ });
t.join();  // 或 t.detach()

2. 互斥量 (<mutex>)

类/函数 说明
std::mutex 基本互斥量
std::recursive_mutex 可递归锁
std::timed_mutex 带超时的互斥量
std::recursive_timed_mutex 可递归+超时
std::shared_mutex (C++17) 读写锁
mutex::lock() 加锁
mutex::unlock() 解锁
mutex::try_lock() 尝试加锁(非阻塞)
RAII 锁管理
说明
std::lock_guard 简单 RAII 锁
std::unique_lock 灵活 RAII 锁(支持延迟、超时等)
std::shared_lock (C++14) RAII 共享锁
std::scoped_lock (C++17) 同时锁定多个互斥量(避免死锁)
cpp 复制代码
std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);  // 自动加/解锁

3. 条件变量 (<condition_variable>)

函数 说明
condition_variable::wait(lock, pred) 等待(阻塞直到被通知且谓词为真)
condition_variable::wait_for() 带超时的等待
condition_variable::wait_until() 等待到指定时间点
condition_variable::notify_one() 唤醒一个等待线程
condition_variable::notify_all() 唤醒所有等待线程
cpp 复制代码
std::mutex mtx;
std::condition_variable cv;
std::unique_lock lk(mtx);
cv.wait(lk, []{ return ready; });  // 等待条件
cv.notify_one();  // 通知

4. 异步任务 (<future>)

类/函数 说明
std::async 异步执行函数,返回 future
std::future<T> 获取异步结果
std::shared_future<T> 可多次获取的 future
std::promise<T> 设置异步值
std::packaged_task 包装可调用对象
future::get() 获取结果(阻塞)
future::wait() 等待结果
future::wait_for() / wait_until() 带超时的等待
cpp 复制代码
std::future<int> fut = std::async([](){ return 42; });
int result = fut.get();  // 阻塞获取

5. 原子操作 (<atomic>)

类型/函数 说明
std::atomic<T> 原子类型
std::atomic_flag 最小原子布尔锁
std::atomic::load() / store() 原子读/写
std::atomic::exchange() 原子交换
std::atomic::compare_exchange_weak/strong() CAS 操作
std::atomic::fetch_add() 原子算术
cpp 复制代码
std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed);

6. 常用辅助函数

函数 说明
std::lock(m1, m2, ...) 同时锁定多个互斥量(避免死锁)
std::try_lock(m1, m2, ...) 尝试锁定多个
std::call_once(flag, func) 仅执行一次

线程管理 (<thread>) 详解

1. std::thread

线程类的核心,表示一个可执行的线程。

构造函数:

cpp 复制代码
std::thread t;                          // 默认构造,不表示任何线程
std::thread t(callable, args...);       // 创建线程并执行 callable
std::thread t(std::move(other));        // 移动构造,转移线程所有权

基本使用:

cpp 复制代码
#include <thread>
#include <iostream>

void func(int x) {
    std::cout << "Value: " << x << std::endl;
}

int main() {
    std::thread t1(func, 42);           // 创建线程执行 func(42)
    std::thread t2([](){                // lambda 表达式
        std::cout << "Lambda thread" << std::endl;
    });
    
    // 注意:必须 join 或 detach,否则析构时程序崩溃
    t1.join();
    t2.join();
}

2. thread::join()

作用:阻塞当前线程,等待目标线程执行完成。

关键点:

  • 每个线程只能 join() 一次
  • 必须在 join() 前调用 joinable() 检查
  • join() 后线程对象不再关联任何线程
cpp 复制代码
#include <thread>
#include <chrono>
#include <iostream>

void slow_task() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Task completed" << std::endl;
}

int main() {
    std::thread t(slow_task);
    
    std::cout << "Waiting for thread..." << std::endl;
    t.join();  // 主线程阻塞,直到 slow_task 完成
    std::cout << "Thread finished" << std::endl;
    
    // t.join();  // 错误!不能重复 join
    return 0;
}

输出:

复制代码
Waiting for thread...
(等待2秒)
Task completed
Thread finished

3. thread::detach()

作用 :将线程分离,使其在后台独立运行,不再能被 join()

关键点:

  • 分离后线程对象与线程执行分离
  • 主线程不会等待分离的线程
  • 线程会在执行完成后自动回收资源
  • 分离后不能再 join() 或重新 detach()
cpp 复制代码
#include <thread>
#include <chrono>
#include <iostream>

void background_task() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Background work done" << std::endl;
}

int main() {
    std::thread t(background_task);
    t.detach();  // 线程分离,独立运行
    
    std::cout << "Main thread continues..." << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 等待后台线程完成
    
    // t.join();  // 错误!detach 后不能再 join
    // t.detach(); // 错误!不能重复 detach
    
    return 0;
}

输出:

复制代码
Main thread continues...
(1秒后)
Background work done

⚠️ 警告:如果主线程提前结束,分离的线程可能被强制终止:

cpp 复制代码
int main() {
    std::thread t([]{
        std::this_thread::sleep_for(std::chrono::seconds(10));
        std::cout << "Never printed" << std::endl;
    });
    t.detach();
    return 0;  // 主线程立即结束,后台线程被强制终止
}

4. thread::joinable()

作用:检查线程对象是否关联了一个可执行的线程。

返回 true 的情况:

  • 线程被创建但未 join()detach()
  • 移动构造后,源线程变为不可 joinable

返回 false 的情况:

  • 默认构造的线程对象
  • 已经 join()
  • 已经 detach()
  • 被移动后的源线程
cpp 复制代码
#include <thread>
#include <iostream>

int main() {
    std::thread t1;                      // 默认构造
    std::thread t2([]{});                // 有线程关联
    std::thread t3([]{});                // 有线程关联
    
    std::cout << "t1 joinable: " << t1.joinable() << std::endl;  // 0 (false)
    std::cout << "t2 joinable: " << t2.joinable() << std::endl;  // 1 (true)
    
    t2.join();                           // 等待完成
    std::cout << "After join, t2 joinable: " << t2.joinable() << std::endl;  // 0
    
    t3.detach();                         // 分离
    std::cout << "After detach, t3 joinable: " << t3.joinable() << std::endl;  // 0
    
    return 0;
}

最佳实践:

cpp 复制代码
if (t.joinable()) {
    t.join();  // 安全地 join
}

5. std::this_thread::get_id()

作用:获取当前线程的唯一标识符。

cpp 复制代码
#include <thread>
#include <iostream>

int main() {
    std::cout << "Main thread ID: " << std::this_thread::get_id() << std::endl;
    
    std::thread t([]{
        std::cout << "Worker thread ID: " << std::this_thread::get_id() << std::endl;
        std::cout << "Is main? " << (std::this_thread::get_id() == std::thread::id()) << std::endl;
    });
    
    std::cout << "Worker thread object ID: " << t.get_id() << std::endl;
    t.join();
    
    // 默认构造的 thread ID
    std::thread empty;
    std::cout << "Empty thread ID: " << empty.get_id() << std::endl;  // 0 或特殊值
}

输出示例:

复制代码
Main thread ID: 140736512397120
Worker thread object ID: 140736512397120
Worker thread ID: 123145476907008
Is main? 0
Empty thread ID: thread::id of a non-executing thread

用途:

  • 调试和日志记录
  • 检查是否在主线程
  • 线程局部存储索引

6. std::this_thread::sleep_for(duration)

作用:阻塞当前线程至少指定的时长。

cpp 复制代码
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono_literals;  // C++14,允许使用 1s, 500ms 等
    
    std::cout << "Start" << std::endl;
    
    std::this_thread::sleep_for(std::chrono::milliseconds(500));  // 500ms
    std::cout << "After 0.5s" << std::endl;
    
    std::this_thread::sleep_for(2s);  // C++14 语法
    std::cout << "After 2s" << std::endl;
    
    std::this_thread::sleep_for(100ms);  // 100ms
    std::cout << "Done" << std::endl;
}

注意事项:

  • 可能睡眠时间比指定值略长(取决于系统调度)
  • 适合轮询检查、节流控制等场景

实际应用:

cpp 复制代码
// 带延迟的重试循环
for (int retry = 0; retry < 5; ++retry) {
    if (try_operation()) break;
    std::this_thread::sleep_for(std::chrono::seconds(1 << retry));  // 指数退避
}

7. std::this_thread::sleep_until(time_point)

作用:阻塞当前线程直到指定的时间点。

cpp 复制代码
#include <thread>
#include <chrono>
#include <iostream>

int main() {
    using namespace std::chrono;
    
    auto start = steady_clock::now();
    
    // 精确睡眠到 1 秒后
    auto wake_time = start + seconds(1);
    std::this_thread::sleep_until(wake_time);
    
    auto elapsed = duration_cast<milliseconds>(steady_clock::now() - start);
    std::cout << "Slept for " << elapsed.count() << "ms" << std::endl;
    
    // 应用到特定时间点(如整秒执行)
    auto next_execution = system_clock::now() + seconds(5);
    std::this_thread::sleep_until(next_execution);
}

sleep_for 的区别:

  • sleep_for:相对时间,"睡眠 X 时长"
  • sleep_until:绝对时间,"睡眠到时间点 T"

实际应用:

cpp 复制代码
// 定时任务:每小时整点执行
void hourly_task() {
    while (true) {
        auto now = std::chrono::system_clock::now();
        auto next_hour = std::chrono::floor<std::chrono::hours>(now) + std::chrono::hours(1);
        std::this_thread::sleep_until(next_hour);
        do_work();
    }
}

8. std::this_thread::yield()

作用:当前线程主动让出 CPU 时间片,提示调度器可以运行其他线程。

关键点:

  • 不是睡眠,只是建议调度器重新调度
  • 可能立即被重新调度(如果没有其他就绪线程)
  • 适合忙等待场景
cpp 复制代码
#include <thread>
#include <atomic>
#include <iostream>

std::atomic<bool> ready{false};

void worker() {
    // 忙等待,但使用 yield 避免占满 CPU
    while (!ready.load()) {
        std::this_thread::yield();  // 让出 CPU,让其他线程运行
    }
    std::cout << "Worker proceeding" << std::endl;
}

int main() {
    std::thread t(worker);
    
    // 模拟准备工作
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ready = true;
    
    t.join();
}

yield() vs sleep_for(0ms) vs 忙等待:

方法 CPU 使用 响应时间 适用场景
忙等待 while(!flag){} 100% 最快 极短等待(微秒级)
yield() 较低 较好 短等待,多线程协作
sleep_for(1ms) 很低 一般 较长等待,省电优先

9. std::hardware_concurrency()

作用:返回系统支持的硬件并发线程数(通常是 CPU 核心数)。

cpp 复制代码
#include <thread>
#include <iostream>

int main() {
    unsigned int cores = std::thread::hardware_concurrency();
    
    std::cout << "Number of cores: " << cores << std::endl;
    std::cout << "Concurrent threads supported: " << cores << std::endl;
    
    if (cores == 0) {
        std::cout << "Information not available" << std::endl;
    }
    
    // 实际应用:创建最优数量的工作线程
    unsigned int thread_count = std::max(1u, cores);
    std::vector<std::thread> workers;
    
    for (unsigned int i = 0; i < thread_count; ++i) {
        workers.emplace_back([i]{
            std::cout << "Worker " << i << " running" << std::endl;
        });
    }
    
    for (auto& t : workers) {
        t.join();
    }
}

注意事项:

  • 可能返回 0(表示信息不可用)
  • 仅作为建议值,不是硬性限制
  • 超线程核心可能被计入
  • 实际最优线程数可能因任务类型而异

最佳实践:

cpp 复制代码
unsigned int optimal_threads = std::thread::hardware_concurrency();
if (optimal_threads == 0) {
    optimal_threads = 4;  // 合理的默认值
}

// CPU 密集型任务:使用核心数
// IO 密集型任务:可以更多(例如 2x 核心数)
unsigned int workers_for_cpu_bound = optimal_threads;
unsigned int workers_for_io_bound = optimal_threads * 2;

完整示例:线程池管理器

cpp 复制代码
#include <thread>
#include <vector>
#include <queue>
#include <functional>
#include <iostream>
#include <chrono>

class SimpleThreadPool {
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    SimpleThreadPool(size_t threads) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [this] { 
                            return stop || !tasks.empty(); 
                        });
                        if (stop && tasks.empty()) return;
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }
    
    void enqueue(std::function<void()> task) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            tasks.push(task);
        }
        cv.notify_one();
    }
    
    ~SimpleThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (std::thread& worker : workers) {
            if (worker.joinable()) worker.join();
        }
    }
};

int main() {
    auto cores = std::thread::hardware_concurrency();
    std::cout << "Using " << cores << " threads" << std::endl;
    
    SimpleThreadPool pool(cores);
    
    for (int i = 0; i < 10; ++i) {
        pool.enqueue([i] {
            std::cout << "Task " << i << " on thread " 
                      << std::this_thread::get_id() << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        });
    }
    
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 0;
}

互斥量 (<mutex>) 详解

一、互斥量类型 (Mutex Types)

  1. std::mutex:基础独占锁,不支持递归和超时,性能最好。
  2. std::recursive_mutex:允许同一线程多次加锁,需对应次数解锁,用于递归函数。
  3. std::timed_mutex:支持 try_lock_for/until 超时尝试,避免无限等待。
  4. std::shared_mutex std::unique_lock (C++17):读写锁,多线程可同时读(共享锁),写独占,读多写少场景适用。

1. std::mutex

最基本的互斥量,提供独占式锁定。

核心特性:

  • 不支持递归锁定(同一线程重复 lock 会导致死锁)
  • 不支持超时
  • 最轻量、性能最好
cpp 复制代码
#include <mutex>
#include <thread>
#include <iostream>

std::mutex mtx;
int shared_counter = 0;

void increment() {
    for (int i = 0; i < 100000; ++i) {
        mtx.lock();
        ++shared_counter;  // 临界区
        mtx.unlock();
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    
    std::cout << "Counter: " << shared_counter << std::endl;  // 200000
}

⚠️ 错误示例 - 递归锁定导致死锁:

cpp 复制代码
std::mutex mtx;

void recursive_func(int depth) {
    mtx.lock();  // 第一次锁定成功
    if (depth > 0) {
        recursive_func(depth - 1);  // 再次 lock(),同一线程,死锁!
    }
    mtx.unlock();
}
// 解决方案:使用 std::recursive_mutex

2. std::recursive_mutex

允许同一线程多次锁定,必须对应相同次数的 unlock()

使用场景: 递归函数、嵌套调用需要同一锁的场景

cpp 复制代码
#include <mutex>
#include <iostream>

std::recursive_mutex rec_mtx;

void recursive_func(int depth) {
    rec_mtx.lock();
    std::cout << "Depth: " << depth << ", Lock count: " 
              << rec_mtx.try_lock() << std::endl;  // try_lock 返回 false 表示已锁
    rec_mtx.unlock();  // 注意:这里 unlock 只是减少计数
    
    if (depth > 0) {
        recursive_func(depth - 1);
    }
}

int main() {
    recursive_func(3);
    
    // 必须解锁相同次数,否则其他线程无法获取
    std::thread t([&]{
        if (rec_mtx.try_lock()) {
            std::cout << "Thread got lock" << std::endl;
            rec_mtx.unlock();
        } else {
            std::cout << "Thread cannot get lock (still locked by main)" << std::endl;
        }
    });
    t.join();
}

性能对比:

cpp 复制代码
// 递归互斥量有额外开销
std::recursive_mutex rec_mtx;  // 稍慢
std::mutex mtx;                 // 更快

// 设计建议:优先考虑重构代码避免递归锁

3. std::timed_mutex

支持超时机制的互斥量。

额外方法:

  • try_lock_for(duration) - 等待一段时间
  • try_lock_until(time_point) - 等待到指定时间点
cpp 复制代码
#include <mutex>
#include <chrono>
#include <thread>
#include <iostream>

std::timed_mutex tmtx;

void worker(int id) {
    using namespace std::chrono_literals;
    
    // 尝试获取锁,最多等待 100ms
    if (tmtx.try_lock_for(100ms)) {
        std::cout << "Thread " << id << " acquired lock" << std::endl;
        std::this_thread::sleep_for(200ms);  // 持有锁 200ms
        tmtx.unlock();
    } else {
        std::cout << "Thread " << id << " timeout, cannot acquire lock" << std::endl;
    }
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();
    t2.join();
}

输出可能:

复制代码
Thread 1 acquired lock
Thread 2 timeout, cannot acquire lock

实用场景 - 避免死锁:

cpp 复制代码
std::timed_mutex mtx1, mtx2;

void safe_lock_both() {
    using namespace std::chrono_literals;
    
    while (true) {
        if (mtx1.try_lock_for(10ms)) {
            if (mtx2.try_lock_for(10ms)) {
                // 成功获取两个锁
                // ... 工作 ...
                mtx2.unlock();
                mtx1.unlock();
                break;
            } else {
                mtx1.unlock();  // 释放已获取的锁,避免死锁
            }
        }
    }
}

4. std::recursive_timed_mutex

结合递归和超时特性。

cpp 复制代码
std::recursive_timed_mutex rec_tmtx;

void recursive_work(int depth) {
    if (rec_tmtx.try_lock_for(std::chrono::milliseconds(50))) {
        std::cout << "Locked at depth " << depth << std::endl;
        if (depth > 0) {
            recursive_work(depth - 1);
        }
        rec_tmtx.unlock();
    } else {
        std::cout << "Timeout at depth " << depth << std::endl;
    }
}

5. std::shared_mutex (C++17)

读写锁,支持共享锁(读)和独占锁(写)。

特性:

  • 多个线程可以同时持有共享锁(读)
  • 独占锁(写)时不允许任何其他锁
  • 适合读多写少的场景
cpp 复制代码
#include <shared_mutex>
#include <mutex>
#include <vector>
#include <thread>

class ThreadSafeVector {
    std::vector<int> data;
    mutable std::shared_mutex mtx;  // mutable 允许 const 方法中加锁
    
public:
    void add(int value) {
        std::unique_lock<std::shared_mutex> lock(mtx);  // 独占锁
        data.push_back(value);
    }
    
    int get(int index) const {
        std::shared_lock<std::shared_mutex> lock(mtx);  // 共享锁
        return data[index];
    }
    
    size_t size() const {
        std::shared_lock<std::shared_mutex> lock(mtx);
        return data.size();
    }
};

int main() {
    ThreadSafeVector vec;
    
    // 多个读线程可以并发
    std::vector<std::thread> readers;
    for (int i = 0; i < 10; ++i) {
        readers.emplace_back([&vec] {
            for (int j = 0; j < 1000; ++j) {
                if (vec.size() > 0) {
                    volatile int v = vec.get(0);  // 读操作
                }
            }
        });
    }
    
    // 写线程需要独占
    std::thread writer([&vec] {
        for (int i = 0; i < 100; ++i) {
            vec.add(i);
        }
    });
    
    for (auto& t : readers) t.join();
    writer.join();
}

二、互斥量操作方法

lock() / unlock() / try_lock()

cpp 复制代码
std::mutex mtx;

// lock() - 阻塞直到获取锁
mtx.lock();
// ... 临界区 ...
mtx.unlock();

// try_lock() - 非阻塞尝试
if (mtx.try_lock()) {
    // 成功获取锁
    // ... 临界区 ...
    mtx.unlock();
} else {
    // 锁已被占用,做其他事
    std::cout << "Busy, do something else" << std::endl;
}

重要规则:

  1. lock()unlock() 必须成对出现
  2. 不要忘记 unlock()(使用 RAII 避免)
  3. 已持有锁的线程再次 lock() 会导致死锁(除非是递归锁)
  4. unlock() 前必须已持有锁,否则未定义行为

三、RAII 锁管理类

1. std::lock_guard

最简单的 RAII 包装器,构造时加锁,析构时解锁。

cpp 复制代码
#include <mutex>

std::mutex mtx;

void safe_function() {
    std::lock_guard<std::mutex> lock(mtx);  // 构造时 lock()
    // ... 临界区,即使抛出异常也会自动 unlock()
}  // 析构时 unlock()

// 不能手动解锁,不能转移所有权

特点:

  • 轻量,零开销(相比手动 lock/unlock)
  • 不能提前解锁
  • 不能移动

2. std::unique_lock

更灵活的 RAII 锁管理器。

灵活性:

  • 可以延迟加锁
  • 可以提前解锁
  • 可以尝试加锁
  • 支持超时
  • 可移动
cpp 复制代码
#include <mutex>
#include <chrono>

std::timed_mutex tmtx;

void flexible_lock() {
    using namespace std::chrono_literals;
    
    // 1. 延迟加锁
    std::unique_lock<std::timed_mutex> lock(tmtx, std::defer_lock);
    
    // 2. 尝试加锁
    if (lock.try_lock()) {
        std::cout << "Got lock immediately" << std::endl;
    }
    
    // 3. 带超时的加锁
    if (lock.try_lock_for(100ms)) {
        std::cout << "Got lock within 100ms" << std::endl;
    }
    
    // 4. 手动解锁
    lock.unlock();
    // ... 不需要锁的代码 ...
    
    // 5. 再次加锁
    lock.lock();
    
    // 6. 转移所有权
    std::unique_lock<std::timed_mutex> lock2 = std::move(lock);  // lock 现在为空
}  // lock2 析构时解锁

std::unique_lock 构造函数选项:

构造函数 行为
unique_lock(mtx) 立即加锁(同 lock_guard)
unique_lock(mtx, defer_lock) 不加锁,稍后手动 lock()
unique_lock(mtx, try_to_lock) 尝试加锁,调用 try_lock()
unique_lock(mtx, adopt_lock) 假设已持有锁(不重复加锁)
unique_lock(mtx, duration) timed_mutex 专用,try_lock_for
unique_lock(mtx, time_point) timed_mutex 专用,try_lock_until

实际应用 - 条件变量:

cpp 复制代码
std::mutex mtx;
std::condition_variable cv;
bool ready = false;

// unique_lock 是条件变量唯一支持的锁类型
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });  // wait 会临时解锁

性能对比:

  • lock_guard - 零开销,应优先使用
  • unique_lock - 略有开销,仅在需要灵活性时使用

3. std::shared_lock (C++14)

配合 shared_mutex 使用的共享锁 RAII 包装器。

cpp 复制代码
#include <shared_mutex>
#include <map>

class ThreadSafeCache {
    std::map<std::string, std::string> cache;
    mutable std::shared_mutex mtx;
    
public:
    // 读操作 - 共享锁
    std::string get(const std::string& key) const {
        std::shared_lock lock(mtx);  // C++17 可省略模板参数
        auto it = cache.find(key);
        return it != cache.end() ? it->second : "";
    }
    
    // 写操作 - 独占锁
    void set(const std::string& key, const std::string& value) {
        std::unique_lock lock(mtx);
        cache[key] = value;
    }
    
    // 升级锁(从共享到独占)
    bool update_if_exists(const std::string& key, const std::string& new_value) {
        std::shared_lock read_lock(mtx);
        auto it = cache.find(key);
        if (it == cache.end()) {
            return false;
        }
        
        // 升级:释放共享锁,获取独占锁
        read_lock.unlock();
        std::unique_lock write_lock(mtx);
        
        // 需要重新验证(可能被其他线程修改)
        it = cache.find(key);
        if (it != cache.end()) {
            it->second = new_value;
            return true;
        }
        return false;
    }
};

4. std::scoped_lock (C++17)

同时锁定多个互斥量,避免死锁。

cpp 复制代码
#include <mutex>

std::mutex mtx1, mtx2, mtx3;

void transfer(int& from, int& to, int amount) {
    // 同时锁定所有互斥量(使用避免死锁算法)
    std::scoped_lock lock(mtx1, mtx2, mtx3);
    
    from -= amount;
    to += amount;
}  // 所有锁自动释放

// 对比传统做法:
void transfer_old(int& from, int& to, int amount) {
    std::lock(mtx1, mtx2, mtx3);  // 同时锁定
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
    std::lock_guard<std::mutex> lock3(mtx3, std::adopt_lock);
    
    from -= amount;
    to += amount;
}

scoped_lock 的优势:

  • 自动使用死锁避免算法
  • 支持任意数量的互斥量
  • 可推导模板参数(C++17)
cpp 复制代码
// C++17 可自动推导类型
std::scoped_lock lock(mtx1, mtx2);  // 不需要 <std::mutex, std::mutex>

四、全局辅助函数

std::lock(m1, m2, ...)

同时锁定多个互斥量,避免死锁。

算法: 使用"全部或无"策略,如果某个锁失败,释放已获取的锁并重试。

cpp 复制代码
std::mutex a, b;

void deadlock_risk() {
    // 危险:可能导致死锁
    a.lock();
    b.lock();  // 如果另一线程先锁 b 再锁 a,死锁
    // ...
    b.unlock();
    a.unlock();
}

void safe_lock() {
    // 安全:同时锁定,避免死锁
    std::lock(a, b);
    
    // 必须配合 adopt_lock 使用 RAII
    std::lock_guard<std::mutex> lock_a(a, std::adopt_lock);
    std::lock_guard<std::mutex> lock_b(b, std::adopt_lock);
    
    // ... 临界区 ...
}  // RAII 自动解锁

scoped_lock 的关系:

  • std::scoped_lock 内部调用 std::lock
  • scoped_lock 更简洁,优先使用

std::try_lock(m1, m2, ...)

尝试锁定多个互斥量,从第一个开始依次 try_lock()

cpp 复制代码
std::mutex m1, m2, m3;

int result = std::try_lock(m1, m2, m3);
if (result == -1) {
    // 所有锁都获取成功
    std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
    std::lock_guard<std::mutex> lock3(m3, std::adopt_lock);
    // ... 工作 ...
} else {
    // result 是失败锁的索引(0-based)
    std::cout << "Failed to lock mutex " << result << std::endl;
    // 成功锁定的需要手动释放(因为没有 RAII)
}

std::call_once(flag, func, args...)

确保函数只执行一次,即使在多线程环境中。

cpp 复制代码
#include <mutex>

std::once_flag init_flag;

class Singleton {
    static Singleton* instance;
    
    static void init() {
        instance = new Singleton();
        std::cout << "Singleton initialized" << std::endl;
    }
    
public:
    static Singleton& get() {
        std::call_once(init_flag, init);
        return *instance;
    }
};

// 线程安全且高效的初始化
void thread_func() {
    auto& s = Singleton::get();  // 只会初始化一次
}

典型应用:

  • 单例模式
  • 延迟初始化
  • 资源只加载一次

五、最佳实践总结

选择指南

场景 推荐方案
简单临界区,不需要灵活性 std::lock_guard
需要提前解锁、尝试锁、超时 std::unique_lock
条件变量等待 std::unique_lock
读多写少 std::shared_mutex + shared_lock
递归函数 std::recursive_mutex
多个互斥量 std::scoped_lock (C++17)
避免死锁 std::lock + adopt_lock
单例/懒初始化 std::call_once

错误示例对比

cpp 复制代码
// ❌ 错误:忘记解锁
void bad() {
    mtx.lock();
    if (error) return;  // 忘记解锁!
    mtx.unlock();
}

// ✅ 正确:使用 RAII
void good() {
    std::lock_guard lock(mtx);
    if (error) return;  // 自动解锁
}

// ❌ 错误:死锁风险
void deadlock() {
    std::lock_guard lock1(mtx1);
    std::lock_guard lock2(mtx2);  // 另一线程可能先锁 mtx2
}

// ✅ 正确:同时锁定
void safe() {
    std::scoped_lock lock(mtx1, mtx2);
}

// ❌ 错误:冗余锁定
void redundant() {
    std::lock_guard lock(mtx);
    mtx.lock();  // 死锁!
}

使用建议

  1. 优先使用 RAII 锁管理lock_guardunique_lock),避免手动 lock/unlock
  2. 避免死锁 :使用 std::lockstd::scoped_lock 同时获取多个锁
  3. 条件变量必须与互斥量配合 ,等待条件时使用 unique_lock
  4. 简单场景用 std::async ,精细控制用 thread + future/promise
  5. 共享数据用原子操作,复杂同步用互斥量+条件变量

条件变量 (<condition_variable>) 详解

一、条件变量类型

1. std::condition_variable

核心特性:

  • 只能与 std::unique_lock<std::mutex> 配合使用
  • 等待、通知机制用于线程间同步
  • 通常用于生产者-消费者模式

工作原理:

条件变量允许一个线程等待某个条件成立,另一个线程在条件满足时通知等待的线程。条件变量本身不维护条件状态,需要程序员手动检查条件。

cpp 复制代码
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>

std::queue<int> queue;
std::mutex mtx;
std::condition_variable cv;
bool done = false;

void producer() {
    for (int i = 0; i < 10; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            queue.push(i);
            std::cout << "Produced: " << i << std::endl;
        }
        cv.notify_one();  // 通知一个等待的消费者
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    {
        std::lock_guard<std::mutex> lock(mtx);
        done = true;
    }
    cv.notify_all();  // 通知所有等待的线程结束
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待条件:队列非空 或 任务完成
        cv.wait(lock, [&] { 
            return !queue.empty() || done; 
        });
        
        if (!queue.empty()) {
            int value = queue.front();
            queue.pop();
            lock.unlock();  // 提前解锁,允许其他消费者并行
            std::cout << "Consumer " << id << " consumed: " << value << std::endl;
        } else if (done) {
            break;
        }
    }
    std::cout << "Consumer " << id << " exiting" << std::endl;
}

int main() {
    std::thread prod(producer);
    std::thread cons1(consumer, 1);
    std::thread cons2(consumer, 2);
    
    prod.join();
    cons1.join();
    cons2.join();
}

2. std::condition_variable_any

核心特性:

  • 可以与任何满足互斥量基本概念的类型配合使用
  • 更通用但性能稍差(通常使用 std::condition_variable 即可)
cpp 复制代码
#include <condition_variable>
#include <shared_mutex>

std::shared_mutex sh_mtx;
std::condition_variable_any cv_any;
bool ready = false;

void reader() {
    std::shared_lock lock(sh_mtx);  // 共享锁
    cv_any.wait(lock, [&] { return ready; });  // condition_variable_any 支持任何锁类型
    // ... 读取操作 ...
}

void writer() {
    std::unique_lock lock(sh_mtx);  // 独占锁
    ready = true;
    cv_any.notify_all();  // 通知所有等待者
}

对比:

特性 condition_variable condition_variable_any
互斥量类型 std::unique_lock<std::mutex> 任何满足 BasicLockable 的类型
性能 更优 稍差(虚函数调用)
使用场景 99% 的情况 特殊需求(如使用 shared_lock)

二、等待函数 (Wait Functions)

1. wait(lock, predicate)wait(lock)

作用: 阻塞当前线程,直到收到通知且条件满足。

wait(lock) - 无谓词版本:

cpp 复制代码
void wait(std::unique_lock<std::mutex>& lock);

wait(lock, predicate) - 带谓词版本(推荐):

cpp 复制代码
template<class Predicate>
void wait(std::unique_lock<std::mutex>& lock, Predicate pred);

关键行为:

  1. 原子地解锁互斥量并阻塞线程
  2. 被通知后,重新获取互斥量
  3. 检查谓词条件(如果提供)
  4. 如果条件不满足,继续等待
cpp 复制代码
std::mutex mtx;
std::condition_variable cv;
bool data_ready = false;
int data = 0;

// ❌ 错误用法:可能虚假唤醒(spurious wakeup)
void bad_wait() {
    std::unique_lock<std::mutex> lock(mtx);
    if (!data_ready) {      // if 而不是 while
        cv.wait(lock);      // 可能虚假唤醒后继续执行
    }
    // 使用 data(可能 data_ready 仍为 false!)
}

// ✅ 正确用法:使用 while 循环检查条件
void good_wait_while() {
    std::unique_lock<std::mutex> lock(mtx);
    while (!data_ready) {   // 每次唤醒都重新检查
        cv.wait(lock);
    }
    // 使用 data(保证条件为真)
}

// ✅ 更佳用法:使用带谓词的 wait(内部就是 while 循环)
void best_wait() {
    std::unique_lock<std::mutex> lock(mtx);
    cv.wait(lock, []{ return data_ready; });  // 等价于上面的 while 循环
    // 使用 data
}

虚假唤醒 (Spurious Wakeup):

  • 即使没有调用 notify,线程也可能被唤醒
  • 这是 POSIX 和 Windows API 允许的行为
  • 必须使用谓词或 while 循环处理

2. wait_for(lock, duration, predicate)

作用: 等待指定的时长,超时后即使条件不满足也会返回。

cpp 复制代码
std::condition_variable cv;
std::mutex mtx;
bool event_occurred = false;

void timed_wait_example() {
    std::unique_lock<std::mutex> lock(mtx);
    
    using namespace std::chrono_literals;
    
    // 版本1:无谓词,返回状态
    auto status = cv.wait_for(lock, 1s);
    if (status == std::cv_status::no_timeout) {
        std::cout << "Notified before timeout" << std::endl;
    } else {
        std::cout << "Timeout occurred" << std::endl;
    }
    
    // 版本2:带谓词,返回布尔值
    bool condition_met = cv.wait_for(lock, 1s, []{ return event_occurred; });
    if (condition_met) {
        std::cout << "Condition became true" << std::endl;
    } else {
        std::cout << "Timeout, condition still false" << std::endl;
    }
}

实际应用 - 带超时的等待:

cpp 复制代码
class TimeoutQueue {
    std::queue<int> queue;
    std::mutex mtx;
    std::condition_variable cv;
    
public:
    bool push(int value, std::chrono::milliseconds timeout) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待队列有空位(假设队列最大容量为 10)
        bool success = cv.wait_for(lock, timeout, [this] {
            return queue.size() < 10;
        });
        
        if (success) {
            queue.push(value);
            cv.notify_one();  // 通知消费者
            return true;
        }
        return false;  // 超时失败
    }
    
    bool pop(int& value, std::chrono::milliseconds timeout) {
        std::unique_lock<std::mutex> lock(mtx);
        
        bool success = cv.wait_for(lock, timeout, [this] {
            return !queue.empty();
        });
        
        if (success) {
            value = queue.front();
            queue.pop();
            cv.notify_one();  // 通知生产者
            return true;
        }
        return false;
    }
};

返回值:

  • wait_for 返回 std::cv_status 枚举:
    • std::cv_status::no_timeout - 被通知唤醒
    • std::cv_status::timeout - 超时唤醒
  • 带谓词版本返回 bool
    • true - 条件满足
    • false - 超时且条件不满足

3. wait_until(lock, time_point, predicate)

作用: 等待到指定的绝对时间点。

cpp 复制代码
void wait_until_example() {
    std::unique_lock<std::mutex> lock(mtx);
    
    using namespace std::chrono;
    
    // 等待到 2025-01-01 00:00:00
    auto target_time = system_clock::from_time_t(time_t(1735689600));
    
    // 版本1:无谓词
    auto status = cv.wait_until(lock, target_time);
    if (status == std::cv_status::no_timeout) {
        std::cout << "Notified before deadline" << std::endl;
    }
    
    // 版本2:带谓词
    bool done = cv.wait_until(lock, target_time, []{ return work_completed; });
    if (done) {
        std::cout << "Work completed on time" << std::endl;
    } else {
        std::cout << "Deadline reached, work incomplete" << std::endl;
    }
}

实际应用 - 定时任务:

cpp 复制代码
class TimedWorker {
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    void run_until(std::chrono::system_clock::time_point deadline) {
        std::unique_lock<std::mutex> lock(mtx);
        
        while (!stop) {
            if (cv.wait_until(lock, deadline, [this] { return stop; })) {
                break;  // 被停止信号唤醒
            }
            
            // 超时,执行定时任务
            std::cout << "Deadline reached, executing task" << std::endl;
            break;
        }
    }
    
    void stop_worker() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
    }
};

三、通知函数 (Notify Functions)

1. notify_one()

作用: 唤醒一个等待该条件变量的线程。

cpp 复制代码
void notify_one_example() {
    std::unique_lock<std::mutex> lock(mtx);
    // ... 修改共享数据 ...
    cv.notify_one();  // 唤醒一个等待线程
}

重要特性:

  • 如果没有等待线程,调用无效(不记录通知)
  • 不会释放锁,锁会在 notify_one 调用后保持
  • 被唤醒的线程需要重新获取锁
cpp 复制代码
std::mutex mtx;
std::condition_variable cv;
std::queue<int> tasks;

void producer() {
    for (int i = 0; i < 100; ++i) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            tasks.push(i);
        }
        cv.notify_one();  // 只唤醒一个消费者
        // 注意:锁已经释放,消费者可以获取锁
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !tasks.empty(); });
        
        int task = tasks.front();
        tasks.pop();
        lock.unlock();  // 提前解锁,允许其他消费者
        
        std::cout << "Consumer " << id << " processing task " << task << std::endl;
        
        if (task == 99) break;
    }
}

2. notify_all()

作用: 唤醒所有等待该条件变量的线程。

cpp 复制代码
void broadcast_example() {
    std::unique_lock<std::mutex> lock(mtx);
    // ... 重要状态改变 ...
    cv.notify_all();  // 唤醒所有等待线程
}

典型应用场景:

cpp 复制代码
class Barrier {
    std::mutex mtx;
    std::condition_variable cv;
    int count;
    const int total;
    
public:
    Barrier(int n) : count(0), total(n) {}
    
    void wait() {
        std::unique_lock<std::mutex> lock(mtx);
        if (++count == total) {
            // 最后一个线程到达,唤醒所有人
            cv.notify_all();
        } else {
            cv.wait(lock, [this] { return count == total; });
        }
    }
};

void worker(Barrier& barrier, int id) {
    std::cout << "Worker " << id << " starting phase 1" << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(100 * id));
    std::cout << "Worker " << id << " waiting at barrier" << std::endl;
    barrier.wait();  // 等待所有线程
    std::cout << "Worker " << id << " proceeding to phase 2" << std::endl;
}

notify_one vs notify_all

场景 推荐
生产者-消费者(单消费者) notify_one
生产者-消费者(多消费者) notify_one(各取一个任务)
多个线程等待同一事件(如屏障) notify_all
状态改变影响所有等待线程 notify_all

性能注意:

  • notify_onenotify_all 开销小
  • 不必要的 notify_all 会导致"惊群效应"(thundering herd)

四、完整应用示例

示例 1:有界阻塞队列(生产者-消费者)

cpp 复制代码
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <iostream>
#include <chrono>

template<typename T>
class BoundedBlockingQueue {
    std::queue<T> queue;
    std::mutex mtx;
    std::condition_variable not_full;
    std::condition_variable not_empty;
    size_t capacity;
    bool shutdown = false;
    
public:
    BoundedBlockingQueue(size_t cap) : capacity(cap) {}
    
    void push(T value) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待队列有空间
        not_full.wait(lock, [this] { 
            return queue.size() < capacity || shutdown; 
        });
        
        if (shutdown) return;
        
        queue.push(std::move(value));
        not_empty.notify_one();  // 通知消费者
    }
    
    bool pop(T& value) {
        std::unique_lock<std::mutex> lock(mtx);
        
        // 等待队列非空或关闭
        not_empty.wait(lock, [this] { 
            return !queue.empty() || shutdown; 
        });
        
        if (shutdown && queue.empty()) return false;
        
        value = std::move(queue.front());
        queue.pop();
        not_full.notify_one();  // 通知生产者
        return true;
    }
    
    void stop() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            shutdown = true;
        }
        not_full.notify_all();
        not_empty.notify_all();
    }
};

int main() {
    BoundedBlockingQueue<int> queue(5);
    
    // 生产者
    std::thread producer([&] {
        for (int i = 0; i < 20; ++i) {
            queue.push(i);
            std::cout << "Produced: " << i << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
        queue.stop();
    });
    
    // 消费者
    std::thread consumer([&] {
        int value;
        while (queue.pop(value)) {
            std::cout << "Consumed: " << value << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        std::cout << "Consumer exiting" << std::endl;
    });
    
    producer.join();
    consumer.join();
    
    return 0;
}

示例 2:读写锁(使用条件变量实现)

cpp 复制代码
class ReadWriteLock {
    std::mutex mtx;
    std::condition_variable cv;
    int readers = 0;
    bool writer = false;
    int pending_writers = 0;
    
public:
    void read_lock() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [this] { 
            return !writer && pending_writers == 0;  // 优先读者策略
        });
        ++readers;
    }
    
    void read_unlock() {
        std::unique_lock<std::mutex> lock(mtx);
        if (--readers == 0) {
            cv.notify_all();  // 可能有等待的写者
        }
    }
    
    void write_lock() {
        std::unique_lock<std::mutex> lock(mtx);
        ++pending_writers;
        cv.wait(lock, [this] { 
            return !writer && readers == 0; 
        });
        --pending_writers;
        writer = true;
    }
    
    void write_unlock() {
        std::unique_lock<std::mutex> lock(mtx);
        writer = false;
        cv.notify_all();  // 唤醒所有等待的读者和写者
    }
};

示例 3:多线程任务池

cpp 复制代码
class ThreadPool {
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    explicit ThreadPool(size_t threads) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [this] { 
                            return stop || !tasks.empty(); 
                        });
                        
                        if (stop && tasks.empty()) return;
                        
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();  // 执行任务时不持有锁
                }
            });
        }
    }
    
    template<class F>
    void enqueue(F&& f) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            tasks.emplace(std::forward<F>(f));
        }
        cv.notify_one();  // 唤醒一个工作线程
    }
    
    ~ThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (auto& worker : workers) {
            if (worker.joinable()) worker.join();
        }
    }
};

五、常见陷阱与最佳实践

最佳实践

  1. 始终使用谓词版本的 wait
cpp 复制代码
// ✅ 正确
cv.wait(lock, []{ return condition; });

// ❌ 错误(可能虚假唤醒)
cv.wait(lock);
  1. 在修改共享状态后立即通知
cpp 复制代码
// ✅ 正确
{
    std::lock_guard lock(mtx);
    data_ready = true;
    cv.notify_one();  // 在锁内通知
}
  1. 使用 unique_lock 而非 lock_guard
cpp 复制代码
// ✅ 正确
std::unique_lock<std::mutex> lock(mtx);

// ❌ 错误(wait 需要 unique_lock)
std::lock_guard<std::mutex> lock(mtx);
cv.wait(lock);  // 编译错误!
  1. 优先使用 notify_one 而非 notify_all
cpp 复制代码
// 除非所有线程都需要被唤醒,否则使用 notify_one
cv.notify_one();  // 更高效

常见错误

  1. 忘记条件检查
cpp 复制代码
// ❌ 危险
cv.wait(lock);  // 虚假唤醒后条件可能不满足
  1. 在锁外通知(可能丢失通知)
cpp 复制代码
// ❌ 危险
data_ready = true;
lock.unlock();
cv.notify_one();  // 通知可能在 wait 之前到达
  1. 等待条件变量时持有其他锁
cpp 复制代码
// ❌ 可能导致死锁
std::lock(mtx1, mtx2);
cv.wait(lock1);  // 锁 mtx2 仍被持有
  1. 在析构函数中未通知
cpp 复制代码
// ❌ 可能永远阻塞
~ThreadPool() {
    // 忘记通知等待线程
}

异步任务 (<future>) 详解

C++ 异步任务 (<future>) 是什么?

核心概念<future> 是 C++11 引入的异步编程工具,让你发起一个可能耗时操作后,不阻塞等待结果,而在未来某个时刻获取它

通俗理解

就像你在餐厅点餐:

  • 同步方式:站在柜台前盯着厨师做完才离开(阻塞)
  • 异步方式:点完拿号回座位,菜好了服务员通知你取餐(非阻塞)

future 就是那个"小票"------代表一个尚未就绪但将来会有的结果

三大核心组件

组件 角色 比喻
std::async 发起异步任务 向厨房下单
std::future 结果凭证 取餐小票
std::promise 手动设置结果 你承诺稍后给结果

基本用法

cpp 复制代码
// 发起异步任务,自动得到 future
std::future<int> fut = std::async([]() {
    std::this_thread::sleep_for(1s);  // 模拟耗时
    return 42;
});

// 主线程可以做其他事...

int result = fut.get();  // 需要结果时阻塞等待

为什么需要它?

  1. 避免阻塞主线程:UI 程序不卡顿、服务器继续处理其他请求
  2. 轻松并行auto f1 = async(task1); auto f2 = async(task2); 两个任务自动并发
  3. 异常传递 :子线程抛出的异常会在 get() 时重新抛出
  4. 超时控制:可以设置最长等待时间

与其他工具的区别

工具 适用场景
std::thread 需要精细控制线程生命周期(如 detach)
std::async + future 只需获取计算结果,不关心线程管理
std::packaged_task 需要把异步任务包装成可调用对象传递
std::promise 需要手动设置异步结果(如回调适配)

一句话总结<future> 让你"发起任务 → 继续干活 → 需要时拿结果",是 C++ 中最简单的异步编程方式。

一、核心类

1. std::future<T>

作用: 表示一个异步操作的结果,可以从异步任务中获取返回值或异常。

核心特性:

  • 只能移动,不能拷贝
  • 只能获取一次结果(get() 后状态失效)
  • 支持等待、超时等操作
cpp 复制代码
#include <future>
#include <iostream>
#include <thread>
#include <chrono>

int long_computation(int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return x * x;
}

int main() {
    // 创建异步任务
    std::future<int> fut = std::async(std::launch::async, long_computation, 5);
    
    std::cout << "Waiting for result..." << std::endl;
    
    // 获取结果(阻塞直到就绪)
    int result = fut.get();
    std::cout << "Result: " << result << std::endl;
    
    // fut.get();  // 错误!只能调用一次
    
    return 0;
}

状态检查:

cpp 复制代码
std::future<int> fut1;  // 默认构造,无效
std::future<int> fut2 = std::async([](){ return 42; });

std::cout << "fut1 valid: " << fut1.valid() << std::endl;  // false
std::cout << "fut2 valid: " << fut2.valid() << std::endl;  // true

fut2.get();
std::cout << "After get, valid: " << fut2.valid() << std::endl;  // false

2. std::shared_future<T>

作用: future 的共享版本,可以多次获取结果,可拷贝。

核心特性:

  • 可以拷贝,多个对象共享同一个异步结果
  • 可以多次调用 get()
  • 适合多个线程等待同一个结果
cpp 复制代码
#include <future>
#include <thread>
#include <vector>

int expensive_calculation() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 100;
}

int main() {
    // 方式1:从 future 转移
    std::future<int> fut = std::async(expensive_calculation);
    std::shared_future<int> shared_fut = std::move(fut);
    
    // 方式2:直接创建(C++11 需要显式类型)
    auto shared_fut2 = std::async(expensive_calculation).share();
    
    // 多个线程可以同时等待同一个结果
    std::vector<std::thread> threads;
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back([shared_fut] {
            int result = shared_fut.get();  // 多次调用没问题
            std::cout << "Thread got: " << result << std::endl;
        });
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    // 可以再次调用 get()
    int result = shared_fut.get();  // 仍然有效
    std::cout << "Main got: " << result << std::endl;
}

future vs shared_future

特性 future shared_future
拷贝 ❌ 只能移动 ✅ 可拷贝
get() 次数 1 次 多次
内存开销 较小 略大
适用场景 单消费者 多消费者

3. std::promise<T>

作用: 存储一个值或异常,通过 future 异步获取。用于手动设置异步结果。

核心概念:

  • promisefuture 是配对使用的
  • promise 设置值,future 获取值
  • 一对一的通道
cpp 复制代码
#include <future>
#include <thread>
#include <iostream>

void process_data(std::promise<int> promise, int data) {
    try {
        // 模拟处理
        if (data < 0) {
            throw std::runtime_error("Negative data!");
        }
        int result = data * 2;
        
        // 设置结果
        promise.set_value(result);
    } catch (...) {
        // 设置异常
        promise.set_exception(std::current_exception());
    }
}

int main() {
    std::promise<int> promise;
    std::future<int> future = promise.get_future();
    
    // 在另一个线程中处理
    std::thread worker(process_data, std::move(promise), 42);
    
    try {
        int result = future.get();  // 等待结果
        std::cout << "Result: " << result << std::endl;
    } catch (const std::exception& e) {
        std::cout << "Exception: " << e.what() << std::endl;
    }
    
    worker.join();
}

多次设置的问题:

cpp 复制代码
std::promise<int> promise;
promise.set_value(42);
// promise.set_value(100);  // 错误!只能设置一次(抛出 future_error)

set_value_at_thread_exit

cpp 复制代码
void thread_func(std::promise<int> promise) {
    // 计算...
    promise.set_value_at_thread_exit(42);  // 线程退出时自动设置
    // 线程继续执行...
    // 线程退出时值才变得可用
}

4. std::packaged_task<T>

作用: 包装可调用对象,使其可以异步执行,并自动提供 future

核心特性:

  • 包装函数、lambda、可调用对象
  • 调用时自动设置 future 的结果
  • 可以被移动但不能拷贝
cpp 复制代码
#include <future>
#include <iostream>
#include <thread>

int add(int a, int b) {
    return a + b;
}

int main() {
    // 包装函数
    std::packaged_task<int(int, int)> task(add);
    std::future<int> future = task.get_future();
    
    // 在另一个线程中执行
    std::thread t(std::move(task), 10, 20);
    
    int result = future.get();  // 获取结果
    std::cout << "10 + 20 = " << result << std::endl;
    
    t.join();
}

包装 lambda:

cpp 复制代码
std::packaged_task<int(int)> task([](int x) {
    return x * x;
});

auto future = task.get_future();

std::thread t(std::move(task), 5);
std::cout << "Square: " << future.get() << std::endl;
t.join();

可重用(重置):

cpp 复制代码
std::packaged_task<int()> task([](){ return 42; });

// 执行第一次
task();
auto result1 = task.get_future().get();  // 42

// 重置后重用
task.reset();  // 清除之前的结果
auto future = task.get_future();
task();
auto result2 = future.get();  // 42

二、创建异步任务的函数

std::async

作用: 异步执行一个函数,返回 future 对象。

启动策略:

cpp 复制代码
std::launch::async      // 立即在新线程中执行
std::launch::deferred   // 延迟执行(调用 get/wait 时执行)
std::launch::async | std::launch::deferred  // 实现选择(默认)
cpp 复制代码
#include <future>
#include <iostream>
#include <chrono>

int heavy_work() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
}

int main() {
    using namespace std::chrono_literals;
    
    // 策略1:async - 立即在新线程执行
    auto fut1 = std::async(std::launch::async, heavy_work);
    std::cout << "Async: " << fut1.get() << std::endl;
    
    // 策略2:deferred - 延迟到 get() 时执行
    auto fut2 = std::async(std::launch::deferred, heavy_work);
    std::this_thread::sleep_for(2s);
    std::cout << "Deferred: " << fut2.get() << std::endl;  // 此时才执行
    
    // 策略3:默认(实现选择)
    auto fut3 = std::async(heavy_work);  // 通常等同于 async|deferred
    
    // 策略4:立即执行,但可以等待
    auto fut4 = std::async(std::launch::async, heavy_work);
    std::future_status status = fut4.wait_for(100ms);
    if (status == std::future_status::timeout) {
        std::cout << "Still running..." << std::endl;
    } else if (status == std::future_status::ready) {
        std::cout << "Result: " << fut4.get() << std::endl;
    }
}

实际应用示例:

cpp 复制代码
// 并发执行多个任务
auto future1 = std::async(std::launch::async, []{ return compute_1(); });
auto future2 = std::async(std::launch::async, []{ return compute_2(); });
auto future3 = std::async(std::launch::async, []{ return compute_3(); });

int result = future1.get() + future2.get() + future3.get();

// 条件执行
auto future = std::async(std::launch::deferred, []{ 
    return expensive_operation(); 
});

if (need_result) {
    int value = future.get();  // 只在需要时计算
}

三、等待函数

1. wait()

阻塞直到结果可用。

cpp 复制代码
std::future<int> fut = std::async([]{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});

std::cout << "Waiting..." << std::endl;
fut.wait();  // 阻塞直到任务完成
std::cout << "Ready!" << std::endl;
std::cout << fut.get() << std::endl;

2. wait_for(duration)

等待一段时间,返回状态。

cpp 复制代码
std::future<int> fut = std::async(std::launch::async, []{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    return 100;
});

using namespace std::chrono_literals;

auto status = fut.wait_for(1s);
if (status == std::future_status::timeout) {
    std::cout << "Task still running after 1s" << std::endl;
} else if (status == std::future_status::ready) {
    std::cout << "Task completed: " << fut.get() << std::endl;
} else if (status == std::future_status::deferred) {
    std::cout << "Task is deferred" << std::endl;
}

std::future_status 枚举:

  • future_status::ready - 结果就绪
  • future_status::timeout - 超时
  • future_status::deferred - 任务延迟执行(未开始)

3. wait_until(time_point)

等待到指定时间点。

cpp 复制代码
auto start = std::chrono::steady_clock::now();
auto deadline = start + std::chrono::seconds(2);

std::future<int> fut = std::async([]{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
});

auto status = fut.wait_until(deadline);
if (status == std::future_status::ready) {
    std::cout << "Result: " << fut.get() << std::endl;
} else {
    std::cout << "Timeout" << std::endl;
}

四、获取结果

get()

阻塞获取结果(只能调用一次)。

cpp 复制代码
std::future<int> fut = std::async([](){ return 123; });
int value = fut.get();  // 阻塞直到就绪

异常处理:

cpp 复制代码
auto fut = std::async([]{
    throw std::runtime_error("Something went wrong");
    return 0;
});

try {
    int result = fut.get();
} catch (const std::exception& e) {
    std::cout << "Caught: " << e.what() << std::endl;
}

五、完整应用示例

示例 1:并行计算求和

cpp 复制代码
#include <future>
#include <vector>
#include <numeric>
#include <iostream>

int sum_range(const std::vector<int>& data, size_t start, size_t end) {
    return std::accumulate(data.begin() + start, data.begin() + end, 0);
}

int parallel_sum(const std::vector<int>& data, size_t num_threads) {
    if (data.empty()) return 0;
    
    size_t chunk_size = (data.size() + num_threads - 1) / num_threads;
    std::vector<std::future<int>> futures;
    
    for (size_t i = 0; i < num_threads; ++i) {
        size_t start = i * chunk_size;
        size_t end = std::min(start + chunk_size, data.size());
        
        if (start >= data.size()) break;
        
        futures.push_back(std::async(std::launch::async, sum_range, 
                                     std::ref(data), start, end));
    }
    
    int total = 0;
    for (auto& fut : futures) {
        total += fut.get();
    }
    
    return total;
}

int main() {
    std::vector<int> data(1000000, 1);  // 一百万个 1
    int sum = parallel_sum(data, 4);
    std::cout << "Sum: " << sum << std::endl;  // 1000000
}

示例 2:生产者-消费者使用 promise

cpp 复制代码
#include <future>
#include <queue>
#include <thread>
#include <iostream>

class RequestQueue {
    std::queue<std::promise<int>> promises;
    std::queue<int> requests;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    void stop_server() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
    }
    
    std::future<int> submit_request(int data) {
        std::promise<int> promise;
        auto future = promise.get_future();
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            requests.push(data);
            promises.push(std::move(promise));
        }
        cv.notify_one();
        
        return future;
    }
    
    void process_requests() {
        while (true) {
            std::unique_lock<std::mutex> lock(mtx);
            cv.wait(lock, [this] { 
                return !requests.empty() || stop; 
            });
            
            if (stop && requests.empty()) break;
            
            int data = requests.front();
            requests.pop();
            auto promise = std::move(promises.front());
            promises.pop();
            
            lock.unlock();
            
            try {
                // 处理请求
                int result = data * 2;
                promise.set_value(result);
            } catch (...) {
                promise.set_exception(std::current_exception());
            }
        }
    }
};

int main() {
    RequestQueue queue;
    std::thread worker(&RequestQueue::process_requests, &queue);
    
    std::vector<std::future<int>> futures;
    for (int i = 1; i <= 5; ++i) {
        futures.push_back(queue.submit_request(i));
    }
    
    for (auto& fut : futures) {
        std::cout << "Result: " << fut.get() << std::endl;
    }
    
    queue.stop_server();
    worker.join();
}

示例 3:使用 packaged_task 实现线程池

cpp 复制代码
#include <future>
#include <queue>
#include <thread>
#include <functional>
#include <vector>
#include <iostream>

class ThreadPool {
    std::vector<std::thread> workers;
    std::queue<std::function<void()>> tasks;
    std::mutex mtx;
    std::condition_variable cv;
    bool stop = false;
    
public:
    ThreadPool(size_t threads) {
        for (size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    {
                        std::unique_lock<std::mutex> lock(mtx);
                        cv.wait(lock, [this] { 
                            return stop || !tasks.empty(); 
                        });
                        
                        if (stop && tasks.empty()) return;
                        
                        task = std::move(tasks.front());
                        tasks.pop();
                    }
                    task();
                }
            });
        }
    }
    
    template<typename F, typename... Args>
    auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        std::future<return_type> result = task->get_future();
        
        {
            std::lock_guard<std::mutex> lock(mtx);
            if (stop) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            tasks.emplace([task]() { (*task)(); });
        }
        cv.notify_one();
        
        return result;
    }
    
    ~ThreadPool() {
        {
            std::lock_guard<std::mutex> lock(mtx);
            stop = true;
        }
        cv.notify_all();
        for (auto& worker : workers) {
            if (worker.joinable()) worker.join();
        }
    }
};

int main() {
    ThreadPool pool(4);
    
    std::vector<std::future<int>> futures;
    for (int i = 0; i < 10; ++i) {
        futures.push_back(pool.enqueue([i] {
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            return i * i;
        }));
    }
    
    for (auto& fut : futures) {
        std::cout << fut.get() << " ";
    }
    std::cout << std::endl;
}

示例 4:超时控制

cpp 复制代码
#include <future>
#include <chrono>
#include <iostream>

std::string fetch_data_from_network() {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    return "Data from server";
}

int main() {
    using namespace std::chrono_literals;
    
    auto future = std::async(std::launch::async, fetch_data_from_network);
    
    // 等待最多 2 秒
    if (future.wait_for(2s) == std::future_status::timeout) {
        std::cout << "Timeout! Operation took too long" << std::endl;
        // 注意:不能取消异步任务,只能忽略结果
        // 任务会在后台继续执行直到完成
    } else {
        std::cout << "Result: " << future.get() << std::endl;
    }
    
    // 实际应用中可能需要处理超时后的清理
    // 例如:设置一个标志让任务提前退出
}

六、常见陷阱与最佳实践

✅ 最佳实践

  1. 优先使用 std::async 而非手动创建线程
cpp 复制代码
// ✅ 更简单、安全
auto fut = std::async(func, args);

// ❌ 需要手动管理
std::thread t(func, args);
t.join();
  1. 使用 shared_future 处理多消费者
cpp 复制代码
// ✅ 多个线程可以获取结果
auto shared_fut = async(func).share();
  1. 合理使用启动策略
cpp 复制代码
// 立即执行
std::async(std::launch::async, func);

// 延迟执行(节省资源)
std::async(std::launch::deferred, func);
  1. 异常安全
cpp 复制代码
// ✅ 异常会自动传播
auto fut = std::async([]{
    if (error) throw std::runtime_error("Error");
    return result;
});

try {
    result = fut.get();
} catch (...) {
    // 处理异常
}

❌ 常见错误

  1. 忘记获取 future 结果
cpp 复制代码
// ❌ 如果 async 使用默认策略,可能不会执行
std::async(func);  // 临时对象析构,可能阻塞或忽略

// ✅ 保存 future 或指定 async 策略
auto fut = std::async(std::launch::async, func);
  1. 多次调用 get()
cpp 复制代码
auto fut = std::async(func);
int a = fut.get();
int b = fut.get();  // 错误!
  1. 死锁
cpp 复制代码
// ❌ 死锁:future 等待自己
auto fut = std::async([&fut] { return fut.get(); }, std::ref(fut));
  1. 忽略超时
cpp 复制代码
// ❌ 可能永远阻塞
fut.wait();

// ✅ 使用超时
if (fut.wait_for(1s) == future_status::timeout) {
    // 处理超时
}

需要我讲解原子操作 (<atomic>) 部分吗?

原子操作 (<atomic>) 详解

一、原子类型

1. std::atomic<T>

作用: 提供对基本类型的原子操作,保证多线程访问时的原子性。

支持的类型:

  • 整型:bool, char, int, long, long long
  • 指针类型
  • 枚举类型
  • 自定义类型(需满足 std::is_trivially_copyable_v<T>
cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>

std::atomic<int> counter{0};  // 原子计数器

void increment(int times) {
    for (int i = 0; i < times; ++i) {
        counter++;  // 原子自增
        // 等价于 counter.fetch_add(1);
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment, 10000);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "Counter: " << counter.load() << std::endl;  // 100000
}

自定义类型:

cpp 复制代码
struct Point {
    int x, y;
    // 必须满足 trivially copyable
};

std::atomic<Point> atom_point;

void update_point() {
    Point p{10, 20};
    atom_point.store(p);  // 原子存储
    
    Point q = atom_point.load();  // 原子读取
    std::cout << "Point: " << q.x << "," << q.y << std::endl;
}

常用特化:

cpp 复制代码
std::atomic_bool      // std::atomic<bool>
std::atomic_char      // std::atomic<char>
std::atomic_int       // std::atomic<int>
std::atomic_long      // std::atomic<long>
std::atomic_llong     // std::atomic<long long>
std::atomic_uint      // std::atomic<unsigned int>
std::atomic_size_t    // std::atomic<size_t>
std::atomic_ptrdiff_t // std::atomic<ptrdiff_t>

2. std::atomic_flag

作用: 最小的原子布尔类型,保证无锁实现。用于实现自旋锁。

核心特性:

  • 只有两个操作:test_and_set()clear()
  • 保证无锁(lock-free)
  • 不能拷贝、移动
cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>

std::atomic_flag lock = ATOMIC_FLAG_INIT;  // 初始化为 false

void spin_lock() {
    while (lock.test_and_set(std::memory_order_acquire)) {
        // 自旋等待
    }
}

void spin_unlock() {
    lock.clear(std::memory_order_release);
}

int shared_data = 0;

void worker(int id) {
    spin_lock();
    shared_data++;
    std::cout << "Thread " << id << " incremented to " << shared_data << std::endl;
    spin_unlock();
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(worker, i);
    }
    
    for (auto& t : threads) {
        t.join();
    }
}

实现自旋锁类:

cpp 复制代码
class SpinLock {
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
    
public:
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire)) {
            // 自旋等待
        }
    }
    
    void unlock() {
        flag.clear(std::memory_order_release);
    }
    
    bool try_lock() {
        return !flag.test_and_set(std::memory_order_acquire);
    }
};

// 使用示例
SpinLock slock;
std::lock_guard<SpinLock> lock(slock);  // RAII 支持

3. std::atomic_ref<T> (C++20)

作用: 对非原子对象提供原子操作。

cpp 复制代码
#if __cplusplus >= 202002L
#include <atomic>

int normal_int = 0;
std::atomic_ref<int> atomic_int(normal_int);

void increment() {
    for (int i = 0; i < 10000; ++i) {
        atomic_int++;  // 原子操作 normal_int
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back(increment);
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << normal_int << std::endl;  // 100000
}
#endif

二、原子操作函数

基本操作

cpp 复制代码
std::atomic<int> a{10};

// 1. store - 存储值
a.store(20);                    // 设置值为 20
a.store(30, std::memory_order_release);  // 带内存顺序

// 2. load - 读取值
int x = a.load();               // 读取当前值
int y = a.load(std::memory_order_acquire);

// 3. exchange - 交换并返回旧值
int old = a.exchange(40);       // 返回 30,设置新值为 40

// 4. compare_exchange_weak/strong - CAS 操作
int expected = 40;
if (a.compare_exchange_strong(expected, 50)) {
    std::cout << "CAS succeeded, new value: " << a.load() << std::endl;
} else {
    std::cout << "CAS failed, expected was: " << expected << std::endl;
}

算术操作(整型特化)

cpp 复制代码
std::atomic<int> counter{0};

// fetch_add - 原子加法,返回旧值
int old = counter.fetch_add(5);   // old = 0, counter = 5
int old = counter.fetch_sub(2);   // old = 5, counter = 3

// 运算符重载
counter++;          // 等价于 fetch_add(1)
++counter;          // 等价于 fetch_add(1) + 1
counter += 3;       // 等价于 fetch_add(3) + 3

// 其他操作
counter.fetch_and(0xFF);   // 位与
counter.fetch_or(0x80);    // 位或
counter.fetch_xor(0xFF);   // 异或

完整示例:

cpp 复制代码
std::atomic<int> value{10};

std::cout << value.fetch_add(5) << std::endl;   // 10 (value = 15)
std::cout << value.fetch_sub(3) << std::endl;   // 15 (value = 12)
std::cout << value.fetch_and(0xF) << std::endl; // 12 (value = 12 & 0xF = 12)
std::cout << value.load() << std::endl;          // 12

value = 20;                       // 隐式 store
int x = value;                    // 隐式 load

指针操作

cpp 复制代码
struct Data { int a, b; };
std::atomic<Data*> ptr{nullptr};

Data data1, data2;

// fetch_add - 指针偏移
ptr.store(&data1);
Data* old = ptr.fetch_add(1);     // 等价于 ptr += sizeof(Data),返回旧指针
Data* new_ptr = ptr.load();       // &data1 + 1

// 比较交换
Data* expected = &data1;
if (ptr.compare_exchange_strong(expected, &data2)) {
    std::cout << "Pointer updated" << std::endl;
}

三、内存顺序

std::memory_order 枚举

内存顺序 说明 适用场景
memory_order_relaxed 最弱顺序,仅保证原子性 计数器
memory_order_consume 依赖顺序(几乎不用) 特殊优化
memory_order_acquire 后续读/写不能重排到此操作前 读锁
memory_order_release 此前读/写不能重排到此操作后 写锁
memory_order_acq_rel acquire + release RMW 操作
memory_order_seq_cst 最强顺序,全局一致(默认) 通用
cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>
#include <vector>

std::atomic<bool> ready{false};
std::atomic<int> data{0};

// 使用 relaxed 的计数器
std::atomic<int> counter{0};

void relaxed_counter() {
    for (int i = 0; i < 10000; ++i) {
        counter.fetch_add(1, std::memory_order_relaxed);
    }
}

// 使用 acquire-release 同步
void producer() {
    data.store(42, std::memory_order_relaxed);
    ready.store(true, std::memory_order_release);  // 发布
}

void consumer() {
    while (!ready.load(std::memory_order_acquire));  // 获取
    // 保证看到 data 的写入
    std::cout << "Data: " << data.load(std::memory_order_relaxed) << std::endl;
}

// 顺序一致性
std::atomic<bool> x{false}, y{false};
std::atomic<int> z{0};

void write_x() {
    x.store(true, std::memory_order_seq_cst);
}

void write_y() {
    y.store(true, std::memory_order_seq_cst);
}

void read_x_then_y() {
    while (!x.load(std::memory_order_seq_cst));
    if (y.load(std::memory_order_seq_cst)) {
        z++;
    }
}

void read_y_then_x() {
    while (!y.load(std::memory_order_seq_cst));
    if (x.load(std::memory_order_seq_cst)) {
        z++;
    }
}

内存顺序选择指南:

cpp 复制代码
// 1. 默认使用 seq_cst(最简单)
counter.fetch_add(1);  // 默认 seq_cst

// 2. 计数器可以用 relaxed
counter.fetch_add(1, std::memory_order_relaxed);

// 3. 生产者-消费者用 release-acquire
producer.store(true, std::memory_order_release);
consumer.load(std::memory_order_acquire);

// 4. 自旋锁用 acquire-release
while (flag.test_and_set(std::memory_order_acquire));
flag.clear(std::memory_order_release);

四、无锁编程函数

is_lock_free() / is_always_lock_free

检查原子操作是否无锁。

cpp 复制代码
std::atomic<int> a;
std::atomic<long long> b;

std::cout << std::boolalpha;
std::cout << "int is lock-free: " << a.is_lock_free() << std::endl;
std::cout << "int always lock-free: " << std::atomic<int>::is_always_lock_free << std::endl;

// 运行时检查
if (a.is_lock_free()) {
    std::cout << "Using lock-free implementation" << std::endl;
} else {
    std::cout << "Using mutex-based implementation" << std::endl;
}

atomic_signal_fence / atomic_thread_fence

内存屏障。

cpp 复制代码
#include <atomic>
#include <thread>

std::atomic<bool> flag{false};
int data = 0;

// 线程 A
void thread_a() {
    data = 42;
    std::atomic_thread_fence(std::memory_order_release);
    flag.store(true, std::memory_order_relaxed);
}

// 线程 B
void thread_b() {
    while (!flag.load(std::memory_order_relaxed));
    std::atomic_thread_fence(std::memory_order_acquire);
    // 保证看到 data = 42
    std::cout << data << std::endl;
}

五、完整应用示例

示例 1:无锁栈

cpp 复制代码
#include <atomic>
#include <iostream>

template<typename T>
class LockFreeStack {
    struct Node {
        T data;
        Node* next;
        Node(const T& d) : data(d), next(nullptr) {}
    };
    
    std::atomic<Node*> head{nullptr};
    
public:
    void push(const T& value) {
        Node* new_node = new Node(value);
        new_node->next = head.load(std::memory_order_relaxed);
        
        // CAS 循环
        while (!head.compare_exchange_weak(new_node->next, new_node,
                                           std::memory_order_release,
                                           std::memory_order_relaxed)) {
            // 失败时 new_node->next 已更新为最新的 head
        }
    }
    
    bool pop(T& result) {
        Node* old_head = head.load(std::memory_order_acquire);
        
        while (old_head && 
               !head.compare_exchange_weak(old_head, old_head->next,
                                          std::memory_order_acquire,
                                          std::memory_order_relaxed)) {
            // 重试
        }
        
        if (old_head) {
            result = old_head->data;
            delete old_head;
            return true;
        }
        return false;
    }
};

int main() {
    LockFreeStack<int> stack;
    
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; ++i) {
        threads.emplace_back([&stack, i] {
            stack.push(i);
        });
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    int value;
    while (stack.pop(value)) {
        std::cout << value << " ";
    }
    std::cout << std::endl;
}

示例 2:无锁队列

cpp 复制代码
#include <atomic>
#include <memory>

template<typename T>
class LockFreeQueue {
    struct Node {
        std::shared_ptr<T> data;
        std::atomic<Node*> next;
        
        Node() : next(nullptr) {}
        Node(const T& value) : data(std::make_shared<T>(value)), next(nullptr) {}
    };
    
    std::atomic<Node*> head;
    std::atomic<Node*> tail;
    
public:
    LockFreeQueue() {
        Node* dummy = new Node();
        head.store(dummy, std::memory_order_relaxed);
        tail.store(dummy, std::memory_order_relaxed);
    }
    
    void enqueue(const T& value) {
        Node* new_node = new Node(value);
        
        while (true) {
            Node* last = tail.load(std::memory_order_acquire);
            Node* next = last->next.load(std::memory_order_acquire);
            
            if (last == tail.load(std::memory_order_acquire)) {
                if (next == nullptr) {
                    if (last->next.compare_exchange_weak(next, new_node,
                                                         std::memory_order_release,
                                                         std::memory_order_relaxed)) {
                        tail.compare_exchange_weak(last, new_node,
                                                   std::memory_order_release,
                                                   std::memory_order_relaxed);
                        break;
                    }
                } else {
                    tail.compare_exchange_weak(last, next,
                                               std::memory_order_release,
                                               std::memory_order_relaxed);
                }
            }
        }
    }
    
    bool dequeue(T& result) {
        while (true) {
            Node* first = head.load(std::memory_order_acquire);
            Node* last = tail.load(std::memory_order_acquire);
            Node* next = first->next.load(std::memory_order_acquire);
            
            if (first == head.load(std::memory_order_acquire)) {
                if (first == last) {
                    if (next == nullptr) {
                        return false;  // 队列为空
                    }
                    tail.compare_exchange_weak(last, next,
                                               std::memory_order_release,
                                               std::memory_order_relaxed);
                } else {
                    if (next->data) {
                        result = *(next->data);
                        if (head.compare_exchange_weak(first, next,
                                                       std::memory_order_release,
                                                       std::memory_order_relaxed)) {
                            delete first;
                            return true;
                        }
                    }
                }
            }
        }
    }
    
    ~LockFreeQueue() {
        T dummy;
        while (dequeue(dummy));
        delete head.load();
    }
};

示例 3:引用计数(智能指针)

cpp 复制代码
#include <atomic>

template<typename T>
class AtomicSharedPtr {
    struct ControlBlock {
        std::atomic<int> ref_count;
        std::atomic<int> weak_count;
        T* ptr;
        
        ControlBlock(T* p) : ref_count(1), weak_count(0), ptr(p) {}
    };
    
    ControlBlock* cb;
    
public:
    AtomicSharedPtr() : cb(nullptr) {}
    
    explicit AtomicSharedPtr(T* ptr) : cb(new ControlBlock(ptr)) {}
    
    AtomicSharedPtr(const AtomicSharedPtr& other) : cb(other.cb) {
        if (cb) {
            cb->ref_count.fetch_add(1, std::memory_order_relaxed);
        }
    }
    
    ~AtomicSharedPtr() {
        if (cb && cb->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
            delete cb->ptr;
            delete cb;
        }
    }
    
    T* operator->() { return cb ? cb->ptr : nullptr; }
    T& operator*() { return *(cb->ptr); }
    
    void reset(T* ptr = nullptr) {
        AtomicSharedPtr(ptr).swap(*this);
    }
    
    void swap(AtomicSharedPtr& other) {
        std::swap(cb, other.cb);
    }
    
    int use_count() const {
        return cb ? cb->ref_count.load(std::memory_order_relaxed) : 0;
    }
};

示例 4:无锁单例模式

cpp 复制代码
#include <atomic>
#include <memory>

class Singleton {
    static std::atomic<Singleton*> instance;
    static std::atomic<bool> initialized;
    
    Singleton() = default;
    
public:
    static Singleton* get() {
        Singleton* ptr = instance.load(std::memory_order_acquire);
        if (!ptr) {
            ptr = new Singleton();
            
            Singleton* expected = nullptr;
            if (instance.compare_exchange_strong(expected, ptr,
                                                std::memory_order_release,
                                                std::memory_order_relaxed)) {
                initialized.store(true, std::memory_order_release);
                return ptr;
            } else {
                delete ptr;
                return instance.load(std::memory_order_acquire);
            }
        }
        return ptr;
    }
    
    static bool is_initialized() {
        return initialized.load(std::memory_order_acquire);
    }
};

std::atomic<Singleton*> Singleton::instance{nullptr};
std::atomic<bool> Singleton::initialized{false};

示例 5:高性能计数器

cpp 复制代码
#include <atomic>
#include <vector>
#include <thread>
#include <iostream>

class Counter {
    // 使用伪共享避免
    struct alignas(64) CacheLine {  // 64 字节对齐
        std::atomic<long long> value{0};
        char padding[64 - sizeof(std::atomic<long long>)];
    };
    
    std::vector<CacheLine> counters;
    int num_threads;
    
public:
    Counter(int threads) : num_threads(threads), counters(threads) {}
    
    void increment(int thread_id) {
        counters[thread_id].value.fetch_add(1, std::memory_order_relaxed);
    }
    
    long long total() const {
        long long sum = 0;
        for (const auto& counter : counters) {
            sum += counter.value.load(std::memory_order_relaxed);
        }
        return sum;
    }
};

int main() {
    const int num_threads = 4;
    const int iterations = 10000000;
    
    Counter counter(num_threads);
    
    std::vector<std::thread> threads;
    for (int i = 0; i < num_threads; ++i) {
        threads.emplace_back([&counter, i, iterations] {
            for (int j = 0; j < iterations; ++j) {
                counter.increment(i);
            }
        });
    }
    
    for (auto& t : threads) {
        t.join();
    }
    
    std::cout << "Total: " << counter.total() << std::endl;  // 40000000
}

六、常见陷阱与最佳实践

最佳实践

  1. 使用默认的内存顺序除非性能关键
cpp 复制代码
// ✅ 简单,正确
counter++;

// ⚠️ 仅在分析后使用 relaxed
counter.fetch_add(1, std::memory_order_relaxed);
  1. 避免伪共享
cpp 复制代码
// ❌ 可能伪共享
struct Bad {
    std::atomic<int> a;
    std::atomic<int> b;  // 与 a 在同一缓存行
};

// ✅ 对齐到缓存行
struct Good {
    alignas(64) std::atomic<int> a;
    alignas(64) std::atomic<int> b;
};
  1. 使用 compare_exchange_weak 循环
cpp 复制代码
// ✅ strong 适用于大多数情况
expected = current.load();
while (!atomic.compare_exchange_weak(expected, new_value)) {
    // 重试
}

// ⚠️ weak 允许虚假失败,但性能更好
  1. 释放内存时注意顺序
cpp 复制代码
// ✅ 确保所有操作完成后再删除
ptr.store(nullptr, std::memory_order_release);
delete old_ptr;  // 安全

常见错误

  1. 忘记原子性
cpp 复制代码
// ❌ 非原子操作
int counter = 0;
counter++;  // 多线程不安全

// ✅ 原子操作
std::atomic<int> counter{0};
counter++;
  1. 内存顺序使用错误
cpp 复制代码
// ❌ 错误的顺序可能导致数据竞争
ready.store(true, std::memory_order_relaxed);
data = 42;  // 可能被重排到 store 之前

// ✅ 正确的顺序
data = 42;
ready.store(true, std::memory_order_release);
  1. CAS 循环错误
cpp 复制代码
// ❌ 没有更新 expected
int expected = 10;
while (!atomic.compare_exchange_weak(expected, 20)) {
    // expected 不会自动更新
}

// ✅ expected 会被自动更新
int expected = 10;
while (!atomic.compare_exchange_weak(expected, 20)) {
    // expected 现在是实际值
}
  1. ABA 问题
cpp 复制代码
// 问题场景:值从 A -> B -> A,CAS 可能错误成功
// 解决方案:使用带 tag 的指针或版本号
struct TaggedPtr {
    void* ptr;
    uint64_t tag;
};
std::atomic<TaggedPtr> atomic_ptr;

总结

类型/函数 用途 注意事项
std::atomic<T> 通用原子类型 自定义类型必须 trivially copyable
std::atomic_flag 自旋锁 唯一保证无锁
load/store 读写 指定内存顺序
exchange 交换 RMW 操作
compare_exchange_* CAS 注意 ABA 问题
fetch_* 算术/位运算 返回旧值
memory_order_* 内存顺序 默认 seq_cst 最安全

写在最后

多线程编程是一把双刃剑,它能显著提升程序性能,但也引入了复杂性。在决定使用多线程之前,一定要先分析程序的瓶颈,确保多线程确实能带来收益。同时,编写多线程代码时要格外小心,仔细考虑所有可能的并发场景。

相关推荐
十五年专注C++开发1 小时前
std::vector<T>到QVector<T>的数据复制方案
c++·vector·iterator模式·qvector
rqtz2 小时前
【机器人】ROS结合Qt开发上位机软件工作空间配置
开发语言·qt·ros
小欣加油12 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
代码中介商13 小时前
C++左值与右值:核心判断法则详解
开发语言·c++
JAVA96513 小时前
JAVA面试-并发篇 05-并发包AQS队列实现原理是什么
java·开发语言·面试
玖玥拾13 小时前
C/C++ 基础笔记(七)
c语言·c++
Halo_tjn13 小时前
反射与设计模式1
java·开发语言·算法
珊瑚里的鱼14 小时前
手撕单例模式中的饿汉模式和懒汉模式,懒汉模式还要再多加一个C++11版本的
开发语言·c++·单例模式
_不会dp不改名_14 小时前
python-opencv环境搭建
开发语言·python·opencv