✅ C++ 多线程实战三板斧:干活、抢东西、传消息
抛开复杂的底层原理(比如内存序、CPU 缓存一致性),我们只讲工程中真正用得上的多线程技能 。
我把 C++ 多线程实战分为三大块:
第一块:怎么干活?(线程的创建与管理)
场景:你有一堆任务要跑,想让它们同时干,别排队。
1. 最基础:std::thread
C++11 引入的"线程原语",功能全但得自己组装。
cpp
#include <iostream>
#include <thread>
// 任务函数
void work(int id) {
for (int i = 0; i < 3; ++i) {
std::cout << "线程 " << id << " 正在干活 " << i << "\n";
}
}
int main() {
std::thread t1(work, 1);
std::thread t2(work, 2);
std::thread t3(work, 3);
t1.join();
t2.join();
t3.join();
}
✅ 实战注意:
- 必须调用
join()(或detach()),否则程序会terminate。 - 优先用
join(),detach()容易导致资源泄漏或未定义行为。
2. 偷懒神器:std::async
如果你只是想"异步跑个函数,等结果",这是最简单的。
cpp
#include <iostream>
#include <future>
int calc() {
int sum = 0;
for (int i = 0; i < 1000000; ++i) sum += i;
return sum;
}
int main() {
// 异步计算
std::future<int> fut = std::async(std::launch::async, calc);
std::cout << "正在等结果...\n";
int result = fut.get();
std::cout << "结果是:" << result << "\n"; // 500000500000
}
✅ 实战注意:
- 务必写
std::launch::async,否则可能同步执行(不新开线程)! fut.get()会阻塞,直到任务完成。
第二块:怎么抢东西?(共享资源竞争)
场景:多个线程要改同一个变量(比如余额、计数器),不加锁会出大问题。
1. 最常用:std::mutex + std::lock_guard
就像"银行柜台",一次只能一个人进去。
cpp
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
int balance = 100;
std::mutex mtx;
void withdraw(int id, int amount) {
std::lock_guard<std::mutex> lock(mtx); // 自动加锁/解锁
if (balance >= amount) {
balance -= amount;
std::cout << "线程 " << id << " 取了 " << amount
<< ",余额 " << balance << "\n";
} else {
std::cout << "线程 " << id << " 余额不足!\n";
}
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back(withdraw, i, 10);
}
for (auto& t : threads) t.join();
}
✅ 实战注意:
- 永远用
lock_guard,别手动lock()/unlock()。 - 锁的范围越小越好(只包住"改数据"的代码)。
2. 高性能替代:std::atomic
如果只是操作单个整数/指针,原子操作比锁快得多。
cpp
#include <atomic>
#include <thread>
#include <iostream>
std::atomic<int> counter(0);
void add() {
for (int i = 0; i < 100000; ++i) {
counter++; // 线程安全的自增
}
}
int main() {
std::thread t1(add), t2(add);
t1.join(); t2.join();
std::cout << "counter = " << counter << "\n"; // 一定是 200000
}
✅ 实战注意:
- 仅适用于简单操作(++、--、load、store)。
- 复杂逻辑(如"if-then-update")仍需用
mutex。
第三块:怎么传消息?(线程间通信)
场景:线程 A 算完数据,要告诉线程 B;或者线程 A 要等线程 B 的结果。
1. 最简单:std::promise / std::future
像"快递单":A 放数据,B 取数据。
cpp
#include <iostream>
#include <thread>
#include <future>
void producer(std::promise<int>&& prom) {
int result = 42;
prom.set_value(result);
}
void consumer(std::future<int>&& fut) {
int result = fut.get();
std::cout << "拿到结果:" << result << "\n";
}
int main() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t1(producer, std::move(prom));
std::thread t2(consumer, std::move(fut));
t1.join(); t2.join();
}
✅ 实战注意:
promise和future不可拷贝,只能移动。- 适合一对一通信。
2. 实战神器:线程安全队列
工程中最常用的"生产者-消费者"模式。
cpp
#include <queue>
#include <mutex>
#include <condition_variable>
template<typename T>
class ThreadSafeQueue {
private:
std::queue<T> q;
mutable std::mutex mtx;
std::condition_variable cv;
public:
void push(T val) {
std::lock_guard<std::mutex> lock(mtx);
q.push(std::move(val));
cv.notify_one();
}
bool try_pop(T& val) {
std::lock_guard<std::mutex> lock(mtx);
if (q.empty()) return false;
val = std::move(q.front());
q.pop();
return true;
}
void wait_and_pop(T& val) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return !q.empty(); });
val = std::move(q.front());
q.pop();
}
};
✅ 实战注意:
condition_variable避免while(true)忙等,省 CPU。- 适合多生产者-多消费者场景(如任务队列、日志系统)。
🧰 总结:C++ 多线程实战三板斧
| 场景 | 推荐工具 | 关键注意点 |
|---|---|---|
| 异步任务 | std::thread / std::async |
join() 别忘;async 加 launch::async |
| 共享资源 | std::mutex + lock_guard |
锁粒度要小;复杂逻辑别用 atomic |
| 线程通信 | promise/future 或 线程安全队列 |
队列用 condition_variable 避免忙等 |
这样讲,是不是更贴近实际干活的场景 了?
------ 不再纠结"memory_order_relaxed 是啥",而是直接告诉你:"这么写,能跑,不出错,性能还行"。