1. 线程基础与Linux原生线程实战
在Linux系统编程中,线程是轻量级的执行单元,相比进程创建和切换的开销更小。我们先从最底层的pthread库开始,这是POSIX标准定义的线程接口。创建线程的基本模式是这样的:
#include <pthread.h> void* thread_func(void* arg) { // 线程执行的代码 return NULL; } int main() { pthread_t tid; pthread_create(&tid, NULL, thread_func, NULL); pthread_join(tid, NULL); // 等待线程结束 return 0; }注意:pthread_create的第四个参数可以传递任意类型的数据给线程函数,但要注意内存生命周期管理。
线程同步是实际开发中最容易出问题的部分。Linux提供了多种同步原语:
- 互斥锁(mutex):保护临界区
- 条件变量(condition variable):线程间通知
- 读写锁(rwlock):读写分离
- 自旋锁(spinlock):短时等待场景
1.1 互斥锁的典型使用场景
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; void* bank_transfer(void* arg) { pthread_mutex_lock(&mutex); // 操作共享账户余额 pthread_mutex_unlock(&mutex); return NULL; }在实际项目中,我强烈建议使用RAII模式封装锁操作,避免忘记解锁的情况。C++11之后的智能锁(std::lock_guard)就是基于这种思想。
2. C++11多线程编程范式
C++11将线程支持纳入了标准库,大大简化了多线程开发。最基本的线程创建方式:
#include <thread> void worker(int param) { // 线程工作代码 } int main() { std::thread t(worker, 42); t.join(); // 等待线程结束 return 0; }C++标准库提供了丰富的线程同步工具:
- std::mutex:互斥锁
- std::condition_variable:条件变量
- std::future/std::promise:异步结果传递
- std::atomic:原子操作
2.1 现代C++线程同步最佳实践
std::mutex mtx; std::condition_variable cv; bool ready = false; void producer() { std::lock_guard<std::mutex> lk(mtx); ready = true; cv.notify_one(); } void consumer() { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, []{return ready;}); // 处理数据 }提示:condition_variable的wait操作会自动释放锁并在唤醒时重新获取,这是它和简单轮询的本质区别。
3. 线程池设计与实现
在实际项目中,频繁创建销毁线程代价很高。线程池是常见的优化方案,其核心组件包括:
- 任务队列
- 工作线程组
- 任务提交接口
- 线程调度策略
3.1 简易线程池实现
class ThreadPool { public: ThreadPool(size_t threads) : stop(false) { for(size_t i = 0; i < threads; ++i) workers.emplace_back([this] { for(;;) { std::function<void()> task; { std::unique_lock<std::mutex> lock(this->queue_mutex); this->condition.wait(lock, [this]{ return this->stop || !this->tasks.empty(); }); if(this->stop && this->tasks.empty()) return; task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } template<class F> void enqueue(F&& f) { { std::unique_lock<std::mutex> lock(queue_mutex); tasks.emplace(std::forward<F>(f)); } condition.notify_one(); } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queue_mutex); stop = true; } condition.notify_all(); for(std::thread &worker: workers) worker.join(); } private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queue_mutex; std::condition_variable condition; bool stop; };这个实现中我特别注意了以下几点:
- 使用std::function包装任务,支持任意可调用对象
- 任务队列使用mutex保护,确保线程安全
- 条件变量避免工作线程空转
- 析构时优雅关闭所有线程
4. 多线程调试与性能优化
多线程程序的调试是公认的难题,以下是我总结的实用技巧:
4.1 常见问题排查表
| 问题现象 | 可能原因 | 排查方法 |
|---|---|---|
| 程序卡死 | 死锁 | gdb attach查看线程堆栈 |
| 数据错乱 | 竞态条件 | 使用ThreadSanitizer工具 |
| 性能下降 | 锁竞争 | perf分析热点,考虑无锁数据结构 |
| 内存泄漏 | 线程未join | valgrind检查,确保所有线程正确回收 |
4.2 性能优化实战
锁粒度优化:将一个大锁拆分为多个小锁
// 优化前 std::mutex big_lock; // 优化后 std::mutex account_lock[N]; // 按账户ID分片无锁编程:对于简单操作使用atomic
std::atomic<int> counter(0); counter.fetch_add(1, std::memory_order_relaxed);任务窃取:平衡各线程负载
// 每个线程有自己的任务队列 // 空闲时可从其他线程队列"窃取"任务
我在实际项目中发现,80%的多线程性能问题都源于不合理的锁策略。通过将全局锁改为细粒度锁后,一个交易系统的吞吐量提升了3倍。
5. C++20新特性与并发编程
C++20引入了多项改进多线程编程的特性:
std::jthread:自动join的线程
std::jthread t([]{ // 线程代码 }); // 析构时自动joinstd::stop_token:优雅停止线程
std::jthread t([](std::stop_token stoken){ while(!stoken.stop_requested()) { // 处理任务 } }); t.request_stop(); // 请求停止std::atomic_ref:对现有变量的原子访问
int data; std::atomic_ref<int> atomic_data(data);std::latch/barrier:线程同步原语
std::latch completion_latch(10); // 等待10个线程 // 每个线程完成后 completion_latch.count_down();
这些新特性让编写安全、高效的多线程程序变得更加容易。特别是在异常安全方面,jthread避免了传统线程可能因为异常导致join被跳过的问题。
6. 实战经验与避坑指南
在多线程开发中,我踩过不少坑,这里分享几个典型案例:
虚假唤醒:条件变量wait必须使用while循环检查条件
// 错误写法 if(not ready) cv.wait(lock); // 正确写法 cv.wait(lock, []{return ready;});锁顺序死锁:多个锁必须按固定顺序获取
// 线程1 lock(A); lock(B); // 线程2 lock(B); // 可能死锁 lock(A); // 解决方案:统一先锁A再锁B线程局部存储:使用thread_local替代全局变量
thread_local int counter = 0; // 每个线程独立实例异步异常安全:确保线程退出时资源释放
std::thread t([&]{ try { // 工作代码 } catch(...) { cleanup(); // 确保异常时也能清理 } });
在最近的一个项目中,我们使用promise/future模式重构了回调地狱式的异步代码,不仅使逻辑更清晰,还减少了50%的竞态条件bug。关键实现如下:
std::future<Result> async_task(Param p) { auto promise = std::make_shared<std::promise<Result>>(); std::future<Result> future = promise->get_future(); std::thread([promise = std::move(promise), p]{ try { Result r = do_work(p); promise->set_value(r); } catch(...) { promise->set_exception(std::current_exception()); } }).detach(); return future; }这种模式特别适合需要链式异步调用的场景,可以通过future.then()实现类似JavaScript Promise的链式调用效果(C++23将正式支持这个特性)。