1. 为什么C++并发编程值得深入学习
在当今多核处理器普及的时代,掌握并发编程已经成为C++开发者的必备技能。我清晰地记得第一次尝试用std::thread创建多个线程时的场景——程序运行结果完全不符合预期,数据竞争导致的结果每次运行都不一样。正是这些痛苦的调试经历让我意识到,系统性地学习C++并发知识有多么重要。
《C++并发实践》第二版这本书之所以值得反复研读,是因为它不仅覆盖了C++11到C++20的现代并发特性,更重要的是提供了大量工程实践中的解决方案。与第一版相比,第二版新增了关于内存模型、原子操作和并行算法的深入讨论,这些都是构建高性能并发系统的关键要素。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 理解C++内存模型的基础
2.1 从硬件架构看内存顺序
现代CPU的乱序执行和缓存体系使得内存访问顺序变得复杂。我曾在一个项目中遇到这样的问题:两个线程通过原子bool变量进行同步,但偶尔还是会出现数据不一致的情况。后来发现是因为没有正确理解内存顺序(memory_order)导致的。
C++标准定义了6种内存顺序:
- memory_order_relaxed
- memory_order_consume
- memory_order_acquire
- memory_order_release
- memory_order_acq_rel
- memory_order_seq_cst
cpp复制std::atomic<bool> ready{false};
int data = 0;
// 线程1
data = 42;
ready.store(true, std::memory_order_release);
// 线程2
while(!ready.load(std::memory_order_acquire));
assert(data == 42); // 这个断言永远不会失败
2.2 原子操作的实际应用场景
原子操作不仅仅是简单的计数器递增。在实际项目中,我经常用它们来实现无锁数据结构。比如一个多生产者单消费者的队列:
cpp复制template<typename T>
class MPSCQueue {
struct Node {
T data;
std::atomic<Node*> next;
};
std::atomic<Node*> head;
std::atomic<Node*> tail;
public:
void push(T value) {
Node* new_node = new Node{std::move(value), nullptr};
Node* old_tail = tail.exchange(new_node, std::memory_order_acq_rel);
old_tail->next.store(new_node, std::memory_order_release);
}
bool pop(T& value) {
Node* old_head = head.load(std::memory_order_relaxed);
if(old_head == nullptr) return false;
head.store(old_head->next.load(std::memory_order_acquire),
std::memory_order_relaxed);
value = std::move(old_head->data);
delete old_head;
return true;
}
};
重要提示:无锁编程虽然性能高,但调试难度极大。建议先用标准库提供的并发容器,确实有性能瓶颈再考虑自实现。
3. 线程管理与同步原语
3.1 std::thread的陷阱与最佳实践
创建线程看起来简单,但有很多细节需要注意。我曾经在一个服务中直接创建了大量线程,结果导致系统资源耗尽。现在我会遵循这些原则:
- 使用线程池而不是直接创建线程
- 为线程设置合理的名称(通过pthread_setname_np或SetThreadDescription)
- 一定要调用join()或detach(),否则程序终止时会调用std::terminate
cpp复制class ThreadGuard {
std::thread t;
public:
explicit ThreadGuard(std::thread t_) : t(std::move(t_)) {
if(!t.joinable()) throw std::logic_error("No thread");
}
~ThreadGuard() {
if(t.joinable()) t.join();
}
ThreadGuard(const ThreadGuard&)=delete;
ThreadGuard& operator=(const ThreadGuard&)=delete;
};
void worker() { /*...*/ }
int main() {
ThreadGuard t(std::thread(worker));
// 线程会在作用域结束时自动join
}
3.2 条件变量的正确使用模式
条件变量(condition_variable)是线程同步的重要工具,但也是最容易用错的原语
