求助:如何用两个互斥锁同步生产者消费者?环形缓冲区线程安全实现
线程安全环形缓冲区同步优化方案
问题根源分析
你的代码被ThreadSanitizer检测到数据竞争,核心原因是:
- 共享数据未被统一保护:缓冲区的
read、write指针是所有生产者、消费者的共享状态,但生产者仅持有mutex_write,消费者仅持有mutex_read,导致读写线程可同时访问/修改这些指针,触发竞争。 - 条件变量使用不规范:调用
pthread_cond_timedwait前未循环检查等待条件,易出现虚假唤醒;超时时间在锁外计算存在时间窗口误差。 - 语法错误:
return retval中retval未定义,注释符号不完整。
修正后的代码
基于给定的rbuf结构体,调整同步逻辑如下(使用单一互斥锁保护所有共享状态,利用原有条件变量实现读写同步):
/* typedef struct { uint8_t* read; uint8_t* write; uint8_t* begin; uint8_t* end; pthread_mutex_t mutex_read; pthread_mutex_t mutex_write; pthread_cond_t signal_read; pthread_cond_t signal_write; } rbuf; */ int producer(rbuf *buffer, void *message, size_t message_len) { int val; struct timespec to; while (1) { // 持有全局锁,保护所有缓冲区共享状态的访问 pthread_mutex_lock(&buffer->mutex_write); // 锁内计算超时时间,避免时间窗口问题 clock_gettime(CLOCK_REALTIME, &to); to.tv_sec += 1; val = write(buffer, message, message_len); if (val == 1) { // 写入成功 // 通知等待的读线程有新数据 pthread_cond_signal(&buffer->signal_read); pthread_mutex_unlock(&buffer->mutex_write); return val; } // 缓冲区满,等待读线程释放空间(带超时) if (pthread_cond_timedwait(&buffer->signal_write, &buffer->mutex_write, &to) == ETIMEDOUT) { pthread_mutex_unlock(&buffer->mutex_write); return val; // 返回写入失败状态 } // 非超时唤醒,释放锁后循环重试 pthread_mutex_unlock(&buffer->mutex_write); } } int consumer(rbuf *buffer, void *buffer_read, size_t *buffer_len) { int val; struct timespec to; while (1) { // 持有同一全局锁,保证读写互斥 pthread_mutex_lock(&buffer->mutex_write); clock_gettime(CLOCK_REALTIME, &to); to.tv_sec += 1; val = read(buffer, buffer_read, buffer_len); if (val == 1) { // 读取成功 // 通知等待的写线程有空闲空间 pthread_cond_signal(&buffer->signal_write); pthread_mutex_unlock(&buffer->mutex_write); return val; } // 缓冲区空,等待写线程写入数据(带超时) if (pthread_cond_timedwait(&buffer->signal_read, &buffer->mutex_write, &to) == ETIMEDOUT) { pthread_mutex_unlock(&buffer->mutex_write); return val; // 返回读取失败状态 } // 非超时唤醒,释放锁后循环重试 pthread_mutex_unlock(&buffer->mutex_write); } }
关键优化点说明
- 统一共享状态保护:所有访问
read、write指针及缓冲区空/满判断的操作,都在mutex_write的临界区内执行,彻底消除跨线程数据竞争。 - 规范条件变量使用:通过外层循环保证
pthread_cond_timedwait返回后重新检查缓冲区状态,避免虚假唤醒导致的逻辑错误。 - 精准超时控制:在锁内获取当前时间并计算超时,确保超时时间的准确性,避免锁外计算带来的时间偏差。
- 语法错误修复:替换未定义的
retval为正确的返回值val,修正注释格式问题。
关于多余mutex的说明
给定结构体中的mutex_read未在上述代码中使用,若需利用该锁,可将其用于消费者线程内部的局部资源互斥(如消费者自身的缓存处理),但缓冲区共享状态的保护必须依赖单一全局锁,否则仍会引发数据竞争。
内容的提问来源于stack exchange,提问作者Miriman
相关产品推荐
相关产品推荐

