Linux 线程同步:解决多线程中的“偶现“ Bug
·
多线程编程中常见的"偶现"问题源于多个线程同时操作共享资源,而线程同步的核心在于控制谁能访问资源以及何时访问。下面我们深入探讨各种同步机制的本质区别。
一、线程安全问题根源
如示例代码所示:
int count = 0;
void* task(void* arg)
{
for(int i = 0; i < 100000; i++)
{
count++; // 非原子操作
}
return nullptr;
}
count++实际包含三个步骤:
- 读取 count 值
- 执行 count + 1
- 写回新值
当多个线程同时执行时,可能出现:
线程A:读取 count=10
线程B:读取 count=10
线程A:写入 11
线程B:写入 11 // 实际应得12
结果常小于预期值 200000。
二、同步的本质
线程同步并非让线程同时运行,而是:让线程有序访问资源
例如:线程A先访问资源,线程B等待。或者线程A完成任务后,通知线程B继续执行
三、互斥锁:基础保护
核心原理:同一时刻仅一个线程访问资源
工作流程
graph LR
A[线程A加锁] --> B[访问资源]
B --> C[释放锁]
C --> D[线程B获得锁]
代码示例
#include <iostream>
#include <pthread.h>
using namespace std;
// 全局共享变量
// 两个线程都会对它进行修改
int count = 0;
// 定义互斥锁(mutex)
// 用来保护共享资源 count
pthread_mutex_t mutex;
// 线程执行函数
// pthread 创建线程时,线程入口函数必须是这种格式
void* task(void* arg)
{
// 循环执行 10 万次
for(int i = 0; i < 100000; i++)
{
// 加锁
// 同一时刻只允许一个线程进入临界区
pthread_mutex_lock(&mutex);
// 临界区代码(共享资源操作)
// 如果不加锁,多个线程同时执行 count++
// 会导致数据错误
count++;
// 解锁
// 释放资源访问权
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
int main()
{
// 定义两个线程对象
pthread_t t1, t2;
// 初始化互斥锁
// nullptr 表示使用默认属性
pthread_mutex_init(&mutex, nullptr);
// 创建线程 t1
// 参数说明:
// &t1 -> 线程对象
// nullptr -> 默认线程属性
// task -> 线程执行函数
// nullptr -> 传递给线程函数的参数
pthread_create(&t1, nullptr, task, nullptr);
// 创建线程 t2
pthread_create(&t2, nullptr, task, nullptr);
// 等待线程 t1 执行结束
// 如果不等待,主线程可能提前退出
pthread_join(t1, nullptr);
// 等待线程 t2 执行结束
pthread_join(t2, nullptr);
// 输出最终结果
// 正常情况下应该输出 200000
cout << count << endl;
// 销毁互斥锁
// 释放系统资源
pthread_mutex_destroy(&mutex);
return 0;
}
缺点
读操作也需要互斥,降低并发效率
四、读写锁:读多写少优化
核心规则
允许多线程并发读
写操作需独占访问
读写锁流程
线程A读
线程B读
线程C读
可以同时进行
但线程D写,那么所有线程必须等待
为什么读写锁性能更高?
因为现实开发中:“读”远远多于“写”
例如:查询用户信息,读取缓存,查看配置。大部分时候只能读取数据,因此读写锁并发能力更强
读操作频率 >> 写操作频率
读写锁可提升 3-5 倍并发性能。
五、条件变量:线程协作
解决场景
生产者-消费者模型中:
// 低效方案
while(true) {
if(数据就绪) 消费; // 空转消耗CPU
}
工作流程
graph TB
A[消费者等待] --> B[生产者工作]
B --> C[发送通知]
C --> D[消费者唤醒]
本质
没有条件满足,线程就睡眠
条件满足后,再唤醒线程
六、信号量:资源配额管理
与互斥锁区别
| 机制 | 准入数量 |
|---|---|
| 互斥锁 | 1 |
| 信号量 | N |
典型应用
连接池管理:sem=5 表示允许5个并发连接
重要性
因为现实开发中:很多资源并不是只能一个线程访问。例如:数据库连接池,线程池,网络连接池都属于:有限资源共享。
七、同步机制对比
| 机制 | 核心目标 | 适用场景 |
|---|---|---|
| 互斥锁 | 资源独占访问 | 通用共享资源保护 |
| 读写锁 | 优化读并发 | 读多写少场景 |
| 条件变量 | 线程状态协作 | 生产者-消费者模型 |
| 信号量 | 资源配额控制 | 连接池/线程池管理 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)