并发编程
基础
- 深入理解 Linux C/C++ 编程:
- 熟练掌握 C/C++: 这是系统编程的基础,特别是指针、内存管理、数据结构。
- 掌握标准 I/O、文件操作、进程控制 (
fork,exec,wait): 理解进程的创建、执行和生命周期。 - 学习信号 (Signals): 理解异步事件处理机制,虽然在现代并发编程中不常用作主要同步手段,但理解其原理很重要。
- 掌握基本的系统调用和库函数。
- 理解计算机体系结构与操作系统原理:
- CPU 架构 (x86-64, ARM): 了解寄存器、缓存 (Cache, L1/L2/L3)、内存层次结构、总线。
- 操作系统核心概念: 进程 (Process) vs. 线程 (Thread)、调度 (Scheduling)、虚拟内存、上下文切换 (Context Switching) 的开销。
- 理解并发 (Concurrency) vs. 并行 (Parallelism): 并发是关于结构(同时处理多个任务),并行是关于执行(同时运行多个任务)。
核心并发机制 - 线程与同步
这是最核心的部分,主要使用 POSIX 线程 (Pthreads)。
-
POSIX 线程 (Pthreads) API:
- 线程创建与管理:
pthread_create,pthread_join,pthread_detach,pthread_exit。 - 线程属性:
pthread_attr_t(栈大小、调度策略等)。 - 线程特定数据 (TSD):
pthread_key_create,pthread_setspecific,pthread_getspecific。
- 线程创建与管理:
-
同步原语 (Synchronization Primitives):
- 互斥锁 (Mutex):
pthread_mutex_t,pthread_mutex_lock,pthread_mutex_unlock。理解死锁、活锁、优先级反转。 - 条件变量 (Condition Variables):
pthread_cond_t,pthread_cond_wait,pthread_cond_signal,pthread_cond_broadcast。掌握“等待 - 通知”模式,与互斥锁配合使用。 - 读写锁 (Read-Write Locks):
pthread_rwlock_t。适用于读多写少的场景。 - 自旋锁 (Spinlocks):
pthread_spinlock_t。在锁竞争不激烈且临界区很短时可能更高效(避免上下文切换开销),但会浪费 CPU 周期。 - 屏障 (Barriers):
pthread_barrier_t。用于同步一组线程在某个点汇合。
- 互斥锁 (Mutex):
-
实践与挑战:
- 经典问题: 实现生产者 - 消费者问题、读者 - 写者问题、哲学家就餐问题。深刻理解同步的必要性。
- 调试: 学习使用
gdb调试多线程程序,理解线程堆栈。 - 工具: 使用
valgrind(特别是helgrind和drd) 检测数据竞争、死锁等并发错误。 - 性能分析: 使用
perf工具分析程序性能,关注上下文切换、缓存未命中等。
高级主题与现代 C++
-
避免低级错误:
- 数据竞争 (Data Race): 多个线程未同步地访问同一内存位置,至少一个是写操作。
- 死锁 (Deadlock): 多个线程相互等待对方持有的锁。
- 活锁 (Livelock): 线程持续改变状态但无法取得进展。
- 优先级反转 (Priority Inversion): 低优先级线程持有高优先级线程需要的锁。
- 虚假唤醒 (Spurious Wakeup): 条件变量
wait可能在没有signal/broadcast的情况下返回。
-
现代 C++ 并发 (C++11 及以后):
<thread>:std::thread,更现代、类型安全的线程接口。<mutex>:std::mutex,std::lock_guard,std::unique_lock,std::shared_mutex(C++17)。<condition_variable>:std::condition_variable。<atomic>:std::atomic<T>。提供无锁 (lock-free) 编程的基础,理解内存序 (memory order -memory_order_relaxed,memory_order_acquire,memory_order_release,memory_order_acq_rel,memory_order_seq_cst) 至关重要。<future>和<promise>:std::async,std::future,std::promise。用于异步任务和结果获取。<shared_mutex>(C++17): 提供共享/独占锁语义。
-
无锁编程 (Lock-Free Programming):
- 基于原子操作和内存序构建高性能数据结构(如无锁队列、栈)。
- 极其复杂且容易出错,通常只在性能要求极高且经过严格测试的场景下使用。优先考虑使用成熟的库。
性能优化与调试
- 性能分析 (Profiling):
perf(Linux): 系统级性能分析神器。分析 CPU 周期、缓存命中/未命中、分支预测、上下文切换、锁争用 (perf lock)。gprof/gperftools(Google Performance Tools): 分析函数调用时间和开销。htop/top: 监控 CPU、内存、线程使用情况。strace/ltrace: 跟踪系统调用和库调用。
- 优化策略:
- 减少锁争用: 缩小临界区、使用细粒度锁、无锁数据结构、避免在锁内进行耗时操作(如 I/O)。
- 减少上下文切换: 合理设置线程数(通常接近 CPU 核心数),避免创建过多线程。
- 提高缓存局部性: 数据访问模式尽量局部化,减少缓存未命中。
- 负载均衡: 确保工作在线程间均匀分配。
- 调试工具:
gdb: 多线程调试 (thread,info threads,thread apply all bt)。valgrind(helgrind,drd): 检测数据竞争、死锁。AddressSanitizer(ASan) +ThreadSanitizer(TSan): Google 的 sanitizer 工具,强烈推荐!TSan 能非常有效地检测数据竞争和死锁,集成到编译器中,性能开销相对可接受。