线程同步之条件变量

文章目录

生产者与消费者

  • 生产者与消费者问题是多线程编程领域中的一个经典问题,主要用来描述一个或多个生产者线程和一个或多个消费者线程共享有限缓冲区资源时的同步问题

问题

  • 问题的核心在于如何保持生产者和消费者之间的协调,确保当缓冲区已满时生产者停止生产,而当缓冲区已空时消费者停止消费,以避免出现资源浪费或死锁的情况
  • 生产者:生成数据,放入共享缓冲区
  • 消费者:从缓冲区取出数据并处理
  • 缓冲区:有限容量的共享资源

挑战

  • 互斥访问:防止同时读写造成数据不一致
  • 同步协调:
    • 缓冲区满时,生产者应等待
    • 缓冲区空时,消费者应等待

条件变量

  • 条件变量是一种线程同步机制,允许线程在某个条件不满足时阻塞等待,当条件满足时被其他线程唤醒

特性

  • 必须与互斥锁配合使用,条件变量本身不提供互斥保护
  • 用于线程间的通信与协调
  • 解决"忙等待"问题,提高效率

修改条件(谓词)必须在互斥锁保护下

函数接口

初始化

c 复制代码
#include <pthread.h>
// 静态初始化(全局/静态变量)
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

// 动态初始化(栈/堆变量)
int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

等待条件(阻塞)

c 复制代码
#include <pthread.h>
int pthread_cond_wait(pthread_cond_t *cond,  pthread_mutex_t *mutex);
  • 执行过程(原子操作):
    • 解锁互斥量 mutex
    • 阻塞线程,等待条件变量 cond 的信号
    • 收到信号后,重新锁定 mutex
    • 返回

发送信号

c 复制代码
#include <pthread.h>
int pthread_cond_signal(pthread_cond_t *cond);   // 唤醒一个等待线程,适合只有一个线程能处理的情况
int pthread_cond_broadcast(pthread_cond_t *cond); // 唤醒所有等待线程,适合多个线程都能处理的情况

接收信号(带超时的等待)

c 复制代码
int pthread_cond_timedwait(pthread_cond_t *cond,
                          pthread_mutex_t *mutex,
                          const struct timespec *abstime);
int pthread_cond_wait(pthread_cond_t *restrict cond,
                    pthread_mutex_t *restrict mutex);

销毁

c 复制代码
int pthread_cond_destroy(pthread_cond_t *cond);
  • 销毁前确保没有线程在等待,确保所有线程都已退出或不再等待

应用模板

c 复制代码
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int condition = 0;  // 条件变量关联的谓词

// 等待线程
pthread_mutex_lock(&mutex);
// 必须用while循环!
while (!condition) {  
    pthread_cond_wait(&cond, &mutex);
}
// 执行操作...
pthread_mutex_unlock(&mutex);

// 通知线程
pthread_mutex_lock(&mutex);
condition = 1;  // 修改条件
pthread_cond_signal(&cond);  // 或 broadcast
pthread_mutex_unlock(&mutex);

示例

c 复制代码
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int count = 0;      // 当前缓冲区数据数量
int in = 0;         // 生产者插入位置
int out = 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* arg) {
    int item;
    while(1) {
        item = rand() % 1000;  // 生产数据
        
        pthread_mutex_lock(&mutex);
        
        // 缓冲区满则等待
        while(count == BUFFER_SIZE) {
            pthread_cond_wait(&not_full, &mutex);
        }
        
        // 生产数据
        buffer[in] = item;
        in = (in + 1) % BUFFER_SIZE;
        count++;
        
        printf("生产者: 生产 %d, 当前数量: %d\n", item, count);
        
        // 通知消费者
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&mutex);
        
        sleep(1);  // 模拟生产耗时
    }
    return NULL;
}

// 消费者
void* consumer(void* arg) {
    int item;
    while(1) {
        pthread_mutex_lock(&mutex);
        
        // 缓冲区空则等待
        while(count == 0) {
            pthread_cond_wait(&not_empty, &mutex);
        }
        
        // 消费数据
        item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        count--;
        
        printf("消费者: 消费 %d, 当前数量: %d\n", item, count);
        
        // 通知生产者
        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&mutex);
        
        sleep(2);  // 模拟消费耗时
    }
    return NULL;
}

虚假唤醒

  • 即使没有线程调用pthread_cond_signal()或pthread_cond_broadcast(),等待在条件变量上的线程也可能被唤醒
  • 虚假唤醒的原因:
    • 多处理器系统的实现细节
    • 信号处理中断
    • 条件变量实现的复杂性

解决虚假唤醒

c 复制代码
// 错误:使用if可能错过检查
pthread_mutex_lock(&mutex);
if (count == 0) {
    pthread_cond_wait(&cond, &mutex);
}
// 这里可能count仍然为0!

// 正确:使用while确保条件真正满足
pthread_mutex_lock(&mutex);
while (count == 0) {
    pthread_cond_wait(&cond, &mutex);
}
// 这里count一定不为0
相关推荐
烛衔溟12 小时前
C语言并发编程:Windows线程
c语言·c++·windows·性能优化·多线程·并发编程·线程同步
我在人间贩卖青春1 天前
线程同步之读写锁
线程同步·读写锁
我在人间贩卖青春2 天前
线程同步之互斥量
互斥锁·线程同步·互斥量
工程师00722 天前
线程同步的意义
c#·锁机制·线程同步
喵手1 个月前
线程同步:确保多线程环境中的数据一致性!
java·线程同步
egoist20232 个月前
[linux仓库]线程同步与生产者消费者模型[线程·陆]
linux·c语言·开发语言·线程同步·阻塞队列·生产者消费者模型
阿巴~阿巴~2 个月前
Linux同步机制:POSIX 信号量 与 SystemV信号量 的 对比
linux·服务器·线程·信号量·线程同步·posix·system v
hour_go3 个月前
C++多线程编程入门实战
c++·并发编程·互斥锁·线程同步·原子操作
维度攻城狮3 个月前
C++中的多线程编程及线程同步
开发语言·c++·性能优化·多线程·线程同步