C++11 首次将并发与多线程 纳入标准库(<thread>、<mutex>、<condition_variable>、<future> 等头文件),结束了此前依赖 POSIX Threads(pthread)、Windows API 等平台特定接口的局面,实现了跨平台的多线程编程。
一. 线程的基础概念
在深入 API 前,先明确多线程的基础概念:
| 概念 | 说明 |
|---|---|
| 线程(Thread) | 进程内的执行流,共享进程资源(内存、文件句柄),但有独立的栈、寄存器。 |
| 数据竞争(Data Race) | 多个线程同时访问同一共享资源,且至少有一个线程是写操作,导致未定义行为。 |
| 同步(Synchronization) | 协调线程执行顺序 / 访问资源的方式(如互斥锁、条件变量),避免数据竞争。 |
| 原子操作(Atomic) | 不可中断的操作(如 ++),无需锁即可保证多线程安全。 |
| 阻塞(Blocking) | 线程暂停执行,等待某个条件满足(如锁释放、数据就绪)。 |
二. 核心组件详细解
线程管理
std::thread 是 C++11 封装线程的核心类,用于创建、管理线程的生命周期。
-
创建线程:构造函数接收可调用对象(函数、Lambda、函数对象)及参数。
-
等待线程 :
join()阻塞当前线程,直到目标线程执行完毕。 -
分离线程 :
detach()将线程设为 "后台线程",与主线程分离,无需join()(线程结束后资源自动回收)。 -
线程标识 :
get_id()返回线程 ID(std::thread::id类型)。
c++
#include <iostream>
#include <thread>
#include <chrono>
// 普通函数作为线程入口
void print_num(int num, const std::string& msg) {
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread " << num << ": " << msg << std::endl;
}
int main() {
// 1. 创建线程:传入函数+参数
std::thread t1(print_num, 1, "Hello C++11");
std::thread t2([]() { // Lambda 作为线程入口
std::cout << "Thread 2: Lambda is running\n";
});
// 2. 获取线程ID
std::cout << "t1 ID: " << t1.get_id() << std::endl;
std::cout << "t2 ID: " << t2.get_id() << std::endl;
// 3. 等待线程结束(必须:否则主线程退出会导致程序崩溃)
t1.join();
t2.join();
// 4. 分离线程示例(后台运行)
std::thread t3(print_num, 3, "Detached thread");
t3.detach(); // 分离后,t3不再joinable()
std::cout << "Main thread exit pre \n";
// 给分离线程一点执行时间(否则主线程退出太快,分离线程可能没执行)
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "Main thread exit after \n";
return 0;
}
互斥锁
(1) 基础互斥锁 std::mutex
核心方法:
lock():加锁(若锁已被占用,阻塞至锁释放);unlock():解锁(必须与lock()配对,否则死锁);try_lock():尝试加锁(成功返回 true,失败返回 false,不阻塞)。
c++
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int g_count = 0;
mutex g_mutex;
void increment() {
for (int i = 0; i < 100000; ++i) {
g_mutex.lock(); // 加锁
++g_count; // 临界区(共享资源操作)
g_mutex.unlock(); // 解锁
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "Final count: " << g_count << endl; // 预期 200000
return 0;
}
(2)智能锁:std::lock_guard(推荐)
std::lock_guard 是 RAII 封装的互斥锁,构造时加锁,析构时自动解锁,避免手动 unlock() 遗漏导致死锁。
c++
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int g_count = 0;
mutex g_mutex;
void increment() {
for (int i = 0; i < 100000; ++i) {
lock_guard<mutex> lock(g_mutex); // 构造加锁,出作用域自动解锁
++g_count;
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "Final count: " << g_count << endl; // 预期 200000
return 0;
}
(3)灵活锁:std::unique_lock
- 延迟加锁(
std::defer_lock); - 手动加锁 / 解锁(
lock()/unlock()); - 转移锁所有权(
std::move); - 配合条件变量使用。
c++
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
int g_count = 0;
mutex g_mutex;
void increment() {
for (int i = 0; i < 100000; ++i) {
unique_lock<mutex> lock(g_mutex, defer_lock); // 延迟加锁
lock.lock(); // 手动加锁
++g_count;
lock.unlock(); // 手动解锁(可提前释放锁,提升并发)
}
}
void increment2() {
for (int i = 0; i < 100000; ++i) {
unique_lock<mutex> lock(g_mutex);
++g_count;
}
}
int main() {
thread t1(increment);
thread t2(increment2);
t1.join();
t2.join();
cout << "Final count: " << g_count << endl; // 预期 200000
return 0;
}
(4)其他互斥锁类型
| 类型 | 特点 |
|---|---|
std::recursive_mutex |
递归互斥锁,允许同一线程多次加锁(需对应次数解锁),适合递归函数 |
std::timed_mutex |
带超时的互斥锁,try_lock_for()/try_lock_until() 支持超时等待 |
std::recursive_timed_mutex |
递归 + 超时的互斥锁 |
条件变量 std::condition_variable
<condition_variable> 头文件提供条件变量,用于线程间同步通信(如 "生产者 - 消费者" 模型),让线程等待某个条件满足后再执行
核心机制:
- 线程 A:等待条件变量,释放锁并阻塞;
- 线程 B:满足条件后,通知条件变量,唤醒线程 A;
- 线程 A 被唤醒后,重新获取锁并检查条件。
示例:生产者 - 消费者模型
c++
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;
queue<int> g_queue;
mutex g_mutex;
condition_variable g_cv;
const int MAX_QUEUE_SIZE = 5;
// 生产者
void producer(int id) {
for (int i = 0; i < 10; ++i) {
unique_lock<mutex> lock(g_mutex);
// 等待:队列未满
g_cv.wait(lock, []() { return g_queue.size() < MAX_QUEUE_SIZE; });
// 生产数据
int data = id * 10 + i;
g_queue.push(data);
cout << "Producer " << id << " push: " << data << endl;
// 通知消费者
g_cv.notify_one();
}
}
// 消费者
void consumer(int id) {
for (int i = 0; i < 10; ++i) {
unique_lock<mutex> lock(g_mutex);
// 等待:队列非空
g_cv.wait(lock, []() { return !g_queue.empty(); });
// 消费数据
int data = g_queue.front();
g_queue.pop();
cout << "Consumer " << id << " pop: " << data << endl;
// 通知生产者
g_cv.notify_one();
}
}
int main() {
thread p1(producer, 1);
thread p2(producer, 2);
thread c1(consumer, 1);
thread c2(consumer, 2);
p1.join();
p2.join();
c1.join();
c2.join();
return 0;
}
关键方法:
wait(lock, pred):释放锁并阻塞,被唤醒后重新获取锁,检查pred(条件满足则继续,否则再次阻塞);notify_one():唤醒一个等待的线程;notify_all():唤醒所有等待的线程;wait_for(lock, duration, pred):超时等待,超时后返回pred的结果。
原子操作:std::atomic
<atomic> 头文件提供原子类型,用于无锁的共享资源操作,比互斥锁更高效(底层由 CPU 原子指令实现)。
std::atomic<T> 封装基础类型(int、bool、pointer 等),支持原子的读写、自增、自减等操作。
c++
#include <iostream>
#include <thread>
#include <atomic>
using namespace std;
atomic<int> g_count(0); // 原子变量
void increment() {
for (int i = 0; i < 100000; ++i) {
++g_count; // 原子自增,无需加锁
}
}
int main() {
thread t1(increment);
thread t2(increment);
t1.join();
t2.join();
cout << "Final count: " << g_count << endl; // 预期 200000
return 0;
}
load() |
原子读取值 |
|---|---|
store(val) |
原子写入值 |
exchange(val) |
原子交换值(返回旧值) |
compare_exchange_weak(expected, desired) |
CAS 操作:若当前值等于 expected,则替换为 desired(弱版本可能伪失败) |
compare_exchange_strong(expected, desired) |
CAS 操作(强版本,无伪失败) |
适用场景
- 简单的计数器、标志位(如
atomic<bool> is_running); - 无锁队列、无锁栈等高性能并发数据结构;
- 注意:
std::atomic不支持复杂类型(如std::string),仅支持基础类型和指针。
关键注意事项
-
joinable()检查 :线程对象在join()/detach()前必须是joinable()(即未被移动、未被 join/detach),否则调用join()会崩溃。 -
线程移动 :
std::thread不可拷贝,但可移动(符合 C++11 移动语义):
c++
#include <iostream>
#include <thread>
#include <chrono>
// 普通函数作为线程入口
void print_num(int num, const std::string& msg) {
// 模拟耗时操作
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread " << num << ": " << msg << std::endl;
}
int main() {
// 1. 创建线程:传入函数+参数
std::thread t4(print_num, 4, "Moved thread");
std::thread t5 = std::move(t4); // t4变为非joinable,t5接管线程
t5.join();
}
- 主线程退出 :若主线程先退出,未
join()/detach()的线程会触发std::terminate()终止程序。