C++ 并发与多线程简介
现代处理器拥有多个核心,为了充分利用其全部性能,程序必须能够同时执行多个任务。并发(concurrency)是指程序在重叠的时间段内执行多个任务的能力,而多线程(multithreading)是实现并发的一种方式,程序被划分为多个并行工作的线程。
C++ 对多线程的支持始于 C++11 标准,此后一直在积极发展。在本文中,我们将探讨所有关键方面:从线程创建到复杂的同步模式。
基础:线程的创建与管理
std::thread —— 基本构建块
std::thread 类允许你在单独的线程中启动一个函数或可调用对象。来看一个最简单的例子:
#include <iostream>#include <thread>
void hello() { std::cout << "来自线程的问候 " << std::this_thread::get_id() << std::endl;}
int main() { std::thread t(hello); // 启动线程 t.join(); // 等待线程结束 return 0;}join() 方法强制主线程等待子线程完成。另一种选择是 detach(),它允许线程独立运行,但需要特别小心(线程可能会比创建它的对象存活得更久)。
传递参数与 Lambda 函数
可以向线程传递参数并使用 Lambda 表达式:
#include <thread>#include <string>
void process(int id, const std::string& data) { // 处理数据}
int main() { std::thread t1(process, 1, "数据"); std::thread t2([](int x) { // Lambda 表达式 }, 42); t1.join(); t2.join();}线程同步:保护共享数据
当多个线程访问同一数据时,会出现竞态条件(race condition)。为了防止这种情况,需要使用同步机制。
互斥锁(std::mutex)
互斥锁是一种同步原语,它保证在任何时刻只有一个线程可以拥有它:
#include <iostream>#include <thread>#include <mutex>
std::mutex mtx;int counter = 0;
void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; // 受保护的访问 }}
int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "计数器: " << counter << std::endl; return 0;}std::lock_guard 是一个 RAII 封装器,它在创建时自动锁定互斥锁,在销毁时自动释放。这可以防止意外忘记释放互斥锁。
条件变量(std::condition_variable)
条件变量允许线程等待特定事件的发生:
#include <queue>#include <condition_variable>
std::queue<int> data_queue;std::mutex queue_mutex;std::condition_variable cv;
void producer() { for (int i = 0; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(i); cv.notify_one(); // 通知消费者 }}
void consumer() { while (true) { std::unique_lock<std::mutex> lock(queue_mutex); cv.wait(lock, []{ return !data_queue.empty(); }); int value = data_queue.front(); data_queue.pop(); lock.unlock(); // 处理