在现代计算机体系结构中,多核处理器已经成为主流。为了充分发挥硬件性能,多线程编程显得尤为重要。C++ 作为一门强大而灵活的系统级语言,从 C++11 开始正式引入了标准化的线程库,为开发者提供了创建、管理线程及同步资源的现代化接口。
本文将全面系统地讲解 C++ 多线程编程,包括基础线程操作、数据共享、互斥锁、条件变量、异步任务、原子操作及并发容器等内容,并通过多个实战示例加深理解。
一、C++ 中的线程模型
1.1 什么是线程?
线程是操作系统能够调度的最小执行单元,是进程中的一个执行路径。一个进程可以包含多个线程,它们共享同一个内存空间,但每个线程有自己独立的栈。
1.2 C++ 的线程支持
从 C++11 开始,C++ 标准库提供了如下线程支持:
std::thread:创建与管理线程std::mutex/std::lock_guard:互斥量与自动加锁std::condition_variable:条件变量std::future/std::async:异步任务std::atomic:原子类型,支持无锁编程
二、线程的基本使用
2.1 创建线程
#include <iostream>
#include <thread>
void hello() {
    std::cout << "Hello from thread!" << std::endl;
}
int main() {
    std::thread t(hello);
    t.join(); // 等待线程结束
    return 0;
}2.2 传递参数给线程
void greet(std::string name) {
    std::cout << "Hello, " << name << std::endl;
}
std::thread t(greet, "Alice");注意:需要传引用时需用 std::ref 包装。
void modify(int& x) { x += 10; }
int value = 5;
std::thread t(modify, std::ref(value));2.3 join 与 detach
join():等待线程完成,阻塞调用线程。detach():分离线程,后台运行,生命周期由系统管理。
std::thread t(f);
t.detach(); // 不再受 main 控制,需确保线程中不使用已销毁资源三、共享数据与互斥机制
3.1 多线程共享数据的问题
多个线程对同一资源进行读写操作时,可能导致数据竞争(race condition)。
int counter = 0;
void increment() {
    for (int i = 0; i < 10000; ++i) {
        ++counter;
    }
}多个线程同时修改 counter,结果不确定。
3.2 使用 mutex 加锁
#include <mutex>
std::mutex mtx;
void safe_increment() {
    for (int i = 0; i < 10000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);
        ++counter;
    }
}3.3 lock_guard 与 unique_lock
std::lock_guard:简单的 RAII 锁管理std::unique_lock:支持延迟锁定、手动解锁、条件变量使用
std::unique_lock<std::mutex> lock(mtx);
lock.unlock(); // 可手动释放四、条件变量与线程通信
4.1 生产者消费者模型
#include <condition_variable>
#include <queue>
std::mutex mtx;
std::condition_variable cv;
std::queue<int> buffer;
void producer() {
    for (int i = 0; i < 10; ++i) {
        std::unique_lock<std::mutex> lock(mtx);
        buffer.push(i);
        cv.notify_one();
    }
}
void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !buffer.empty(); });
        int item = buffer.front();
        buffer.pop();
        std::cout << "Consumed: " << item << std::endl;
    }
}4.2 notify_one vs notify_all
notify_one():唤醒一个等待线程notify_all():唤醒所有等待线程
五、异步编程与 future/promise
5.1 std::async
#include <future>
int compute() {
    return 42;
}
int main() {
    std::future<int> fut = std::async(compute);
    int result = fut.get(); // 阻塞直到返回值准备好
}5.2 使用 promise 传递值
void worker(std::promise<int> p) {
    p.set_value(123);
}
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t(worker, std::move(p));
int val = f.get();六、原子操作与无锁编程
6.1 std::atomic 基本用法
#include <atomic>
std::atomic<int> acounter(0);
void task() {
    for (int i = 0; i < 10000; ++i)
        acounter.fetch_add(1);
}常用方法:
fetch_add,fetch_subcompare_exchange_weak/strongload,store
6.2 原子与内存序
std::memory_order 控制内存同步行为:
memory_order_relaxed:不保证同步memory_order_acquire/release:适度同步memory_order_seq_cst:最严格同步,默认
七、并发容器与线程安全 STL
7.1 为什么要并发容器?
STL 中的容器如 vector、map 等并不是线程安全的。
并发容器如:
concurrent_queueconcurrent_vector(TBB 或 boost)std::atomic<T*>实现的无锁链表等
7.2 自己实现线程安全队列
template <typename T>
class ThreadSafeQueue {
    std::queue<T> q;
    std::mutex mtx;
    std::condition_variable cv;
public:
    void push(T value) {
        std::lock_guard<std::mutex> lock(mtx);
        q.push(std::move(value));
        cv.notify_one();
    }
    T pop() {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [&]{ return !q.empty(); });
        T val = std::move(q.front());
        q.pop();
        return val;
    }
};八、实战案例:并发网页下载器
8.1 设计目标
- 支持多个 URL 同时下载
 - 下载结果保存到 map 中
 - 控制最大线程数量(线程池模型)
 
8.2 关键代码
#include <thread>
#include <map>
#include <mutex>
#include <vector>
#include <curl/curl.h>
std::mutex mtx;
std::map<std::string, std::string> result;
void download(const std::string& url) {
    CURL* curl = curl_easy_init();
    std::string data;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
            [](char* ptr, size_t size, size_t nmemb, void* userdata) -> size_t {
                std::string* s = static_cast<std::string*>(userdata);
                s->append(ptr, size * nmemb);
                return size * nmemb;
            });
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
        curl_easy_perform(curl);
        curl_easy_cleanup(curl);
    }
    std::lock_guard<std::mutex> lock(mtx);
    result[url] = data;
}主程序并发控制:
std::vector<std::string> urls = {"http://a.com", "http://b.com"};
std::vector<std::thread> threads;
for (const auto& url : urls) {
    threads.emplace_back(download, url);
}
for (auto& t : threads) t.join();九、C++20 中的并发增强
9.1 std::jthread
自动 join 的线程,避免遗忘 join()。
std::jthread t([]{ std::cout << "run\n"; });9.2 stop_token 与可中断线程
std::jthread t([](std::stop_token st) {
    while (!st.stop_requested()) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
});
t.request_stop();十、总结与最佳实践
10.1 总结要点
技术点  | 建议  | 
线程创建  | 使用   | 
资源共享  | 
  | 
同步控制  | 
  | 
异步任务  | 
  | 
原子操作  | 使用   | 
并发容器  | 自实现或使用库  | 
线程数量控制  | 推荐封装线程池  | 
10.2 最佳实践建议
- 不要过度创建线程,推荐线程池
 - 多线程代码要尽可能保持无共享状态(状态隔离)
 - 善用 RAII + 
lock_guard管理锁 - 推荐使用 
std::async编写并行任务 - 合理选择同步机制:
mutexvsatomicvscondition_variable 










