C++ 多线程编程学习笔记
一、学习背景
在学习 muduo 网络库的过程中,发现多线程是一个非常重要的基础知识点。这几天通过实践,我对 Linux 下的线程编程有了更深的理解。
二、线程ID的三种获取方式
在学习过程中,我发现线程 ID 有三种不同的获取方式,这点很有意思:
- 系统线程ID - 通过系统调用获取
// 这个是真实的系统级线程ID
int tid = syscall(SYS_gettid);
实验发现:这个 ID 和 ps -eLf
命令看到的 LWP 是一样的!
- pthread线程ID - 通过pthread库获取
// 这个ID在进程内部使用
pthread_t ptid = pthread_self();
注意:这个ID和系统线程ID是不一样的,我测试时发现数值差异很大。
- C++线程ID - C++11的方式
// 这是最新的推荐用法
auto cpp_tid = std::this_thread::get_id();
三、我实现的生产者-消费者模型
经过多次调试,我终于实现了一个完整的生产者-消费者模型:
// 关键的共享数据
#define QUEUE_SIZE 5
int queue[QUEUE_SIZE];
int count = 0;
int front = 0, rear = 0;// 同步工具
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;// 生产者线程
void* producer(void*) {for(int i = 0; i < 20; i++) { // 生产20个数据pthread_mutex_lock(&mutex);while(count == QUEUE_SIZE) {pthread_cond_wait(¬_full, &mutex);}queue[rear] = i;rear = (rear + 1) % QUEUE_SIZE;count++;pthread_cond_signal(¬_empty);pthread_mutex_unlock(&mutex);sleep(1); // 控制生产速度}return NULL;
}
四、遇到的问题和解决方案
-
多消费者问题
- 问题:多个消费者会重复消费同一个数据
- 解决:使用互斥锁保护共享数据,确保原子操作
-
死锁问题
- 问题:忘记释放锁导致死锁
- 解决:养成好习惯,加锁和解锁要配对使用
-
条件变量使用
- 问题:signal和broadcast的选择
- 解决:一般情况用signal就够了,除非需要唤醒所有等待线程
五、调试技巧
- 使用 ps 命令查看线程:
ps -eLf | grep [进程ID]
这个命令让我第一次清晰地看到了线程的层次关系!
- 使用 cout 输出时要小心:
// 多线程下cout可能混乱,建议这样写
cout << "线程 " << tid << ": " << msg << endl;
- 静态初始化的互斥锁和条件变量也需要销毁,这点很容易忽略
- 多线程调试比单线程复杂得多,打日志很重要
- 使用 RAII 技术能大大减少资源泄露的风险
记录时间:2025年4月