You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

单生产者双消费者(缓冲区大小为1)的文件名处理型生产者消费者问题解决方案咨询

解决方案:让两个消费者共享单个缓冲区的文件名

你的核心痛点在于缓冲区仅能存1个文件名,但需要两个消费者都处理它,同时不强制线程执行顺序——这个场景的关键是跟踪消费者的处理完成状态,并用广播唤醒替代单线程唤醒,确保所有消费者都能响应生产事件。下面是具体的实现方案和细节解释:

第一步:扩展共享数据结构

首先要新增一个计数器,用来记录已经处理完当前文件名的消费者数量,同时补充线程安全的退出标志:

// 原共享数据基础上新增
int consumer_processed_count = 0;
volatile int exit_flag = 0; // 用volatile保证多线程下的可见性

第二步:修改生产者线程逻辑

生产者需要等待两个消费者都处理完当前文件,才能生成下一个文件名,并且必须用广播唤醒所有消费者(而非单个):

while (!exit_flag) {
    pthread_mutex_lock(&filenameMutex);
    
    // 循环等待:只有两个消费者都处理完,才能生产下一个文件
    while (consumer_processed_count != 2) {
        pthread_cond_wait(&canProduceFilename, &filenameMutex);
    }
    
    // 读取文件名(注意确保filenameBuffer有足够空间,比如定义为char filenameBuffer[256];)
    scanf("%s", filenameBuffer);
    
    // 检查退出信号
    if (strcmp(filenameBuffer, "EXIT") == 0) {
        exit_flag = 1;
    }
    
    // 重置计数器,准备让消费者处理新文件
    consumer_processed_count = 0;
    // 广播唤醒所有等待的消费者,确保两个线程都能收到通知
    pthread_cond_broadcast(&canConsumeFilename);
    
    pthread_mutex_unlock(&filenameMutex);
}

第三步:修改消费者线程逻辑

每个消费者处理完文件名后递增计数器,当两个消费者都完成时,通知生产者可以继续生产,同时处理退出逻辑:

while (!exit_flag) {
    pthread_mutex_lock(&filenameMutex);
    
    // 循环等待:要么有未处理的文件名,要么触发退出
    while (consumer_processed_count >= 2 && !exit_flag) {
        pthread_cond_wait(&canConsumeFilename, &filenameMutex);
    }
    
    // 触发退出时直接解锁并终止循环
    if (exit_flag) {
        pthread_mutex_unlock(&filenameMutex);
        break;
    }
    
    // 这里执行你的业务逻辑:比如消费者1做文件解析,消费者2做备份
    printf("Consumer %lu processing file: %s\n", pthread_self(), filenameBuffer);
    
    // 标记当前消费者已处理完成
    consumer_processed_count++;
    
    // 当两个消费者都处理完,通知生产者可以生产下一个文件
    if (consumer_processed_count == 2) {
        pthread_cond_signal(&canProduceFilename);
        // 可选:清空缓冲区避免误判,不过生产者会覆盖数据,也可以省略
        // memset(filenameBuffer, 0, sizeof(filenameBuffer));
    }
    
    pthread_mutex_unlock(&filenameMutex);
    
    // 模拟业务处理耗时(根据实际需求删除或修改)
    // sleep(1);
}

关键细节解释

  • 为什么用pthread_cond_broadcastpthread_cond_signal只会唤醒等待队列中的一个线程,而我们需要两个消费者都能处理同一个文件名。广播能确保所有等待canConsumeFilename的线程都收到通知,完全不依赖线程调度顺序。
  • 计数器的作用:用来确保生产者只有在两个消费者都处理完当前文件后,才会生成下一个文件,避免缓冲区被提前覆盖,同时保证每个文件名都被两个消费者处理。
  • while而非if做条件等待:线程可能会被虚假唤醒(比如系统信号干扰),循环判断能确保只有满足真正的条件时才继续执行,避免逻辑错误。
  • 线程安全的退出:用volatile修饰exit_flag,确保所有线程能及时感知到退出信号,不会卡在等待条件上。

额外注意事项

  • 确保filenameBuffer分配了足够的内存,比如char filenameBuffer[256];,避免scanf输入溢出。
  • 所有共享变量的访问必须在互斥锁保护下,包括exit_flagconsumer_processed_count,杜绝竞态条件。
  • 即使两个消费者的业务逻辑耗时差异很大,也不会影响流程:慢的那个处理完成后才会触发生产者生产下一个文件,保证每个文件名都被两个消费者处理到。

内容的提问来源于stack exchange,提问作者Nitrogen

火山引擎 最新活动