汉字魔法师
118.67M · 2026-02-04
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 类型)。
#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 runningn";
});
// 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;
}
std::mutex核心方法:
lock():加锁(若锁已被占用,阻塞至锁释放);unlock():解锁(必须与 lock() 配对,否则死锁);try_lock():尝试加锁(成功返回 true,失败返回 false,不阻塞)。#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;
}
std::lock_guard(推荐)std::lock_guard 是 RAII 封装的互斥锁,构造时加锁,析构时自动解锁,避免手动 unlock() 遗漏导致死锁。
#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;
}
std::unique_lockstd::defer_lock);lock()/unlock());std::move);#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;
}
| 类型 | 特点 |
|---|---|
std::recursive_mutex | 递归互斥锁,允许同一线程多次加锁(需对应次数解锁),适合递归函数 |
std::timed_mutex | 带超时的互斥锁,try_lock_for()/try_lock_until() 支持超时等待 |
std::recursive_timed_mutex | 递归 + 超时的互斥锁 |
std::condition_variable<condition_variable> 头文件提供条件变量,用于线程间同步通信(如 “生产者 - 消费者” 模型),让线程等待某个条件满足后再执行
核心机制:
示例:生产者 - 消费者模型
#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 等),支持原子的读写、自增、自减等操作。
#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 移动语义):
#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() 终止程序。