C++单生产者多消费者模型阻塞问题求助
问题分析与解决方案
你的问题核心在于消费者线程无法感知生产者已完成生产:当剩余元素数量等于消费者线程数时,部分线程消费完元素后,剩下的线程会因storage暂时为空且没有新的通知而永久阻塞。此外,main函数中storageCV.notify_all()放在join之后,根本无法执行到——此时消费者已经卡住,主线程会一直等待消费者线程结束。
修复步骤
- 添加生产者完成标志:新增全局布尔变量
producer_done,标记生产者是否完成所有元素生产。 - 修改消费者等待条件:将等待条件改为
!storage.empty() || producer_done,确保生产者完成后,消费者能被唤醒并检查剩余元素。 - 调整消费者退出逻辑:唤醒后先判断
producer_done且storage.empty时才退出,否则继续消费剩余元素。 - 修正主线程通知时机:生产者结束后立即设置
producer_done并调用notify_all(),唤醒所有等待的消费者。
修复后的完整代码
#include <chrono> #include <condition_variable> #include <functional> #include <iostream> #include <mutex> #include <thread> #include <vector> #define CONSUMER_COUNT 4 #define STORAGE_SIZE CONSUMER_COUNT * 10000 class Foo { private: int _id; public: Foo(int id) : _id(id) {} int getId() const { return _id; } }; std::vector<Foo> storage; std::mutex storageMutex; std::condition_variable storageCV; bool producer_done = false; // 新增生产者完成标志 void Producer(int limit) { for (int i = 0; i < limit; ++i) { std::lock_guard<std::mutex> lg{storageMutex}; storage.emplace_back(Foo(i)); storageCV.notify_one(); } // 生产完成后设置标志并通知所有消费者 std::lock_guard<std::mutex> lg{storageMutex}; producer_done = true; storageCV.notify_all(); } void Consumer(int id) { while (true) { std::unique_lock<std::mutex> ul{storageMutex}; // 等待条件:有元素可消费 或者 生产者已完成 storageCV.wait(ul, []() { return !storage.empty() || producer_done; }); // 生产者完成且无剩余元素时退出 if (producer_done && storage.empty()) { return; } // 有元素则消费 if (!storage.empty()) { storage.pop_back(); } } } int main(int argc, char *argv[]) { std::vector<std::thread> consumers; consumers.reserve(CONSUMER_COUNT); auto producer = std::thread(Producer, STORAGE_SIZE); for (int i = 0; i < CONSUMER_COUNT; ++i) { consumers.emplace_back(std::thread(Consumer, i)); } producer.join(); for (auto &consumer : consumers) { consumer.join(); } std::cout << "[MAIN] Done!" << std::endl; std::cout << "Storage is left with " << storage.size() << " items!" << std::endl; return 0; }
关键说明
- 线程安全的标志设置:设置
producer_done时必须持有互斥锁,确保消费者线程能看到最新值,避免内存可见性问题。 - 等待条件的完整性:
wait的谓词必须包含producer_done,否则生产者完成后,消费者可能永远阻塞在wait调用上。 - 退出逻辑的合理性:先检查生产者状态和存储状态,避免提前退出导致剩余元素未被消费。
内容的提问来源于stack exchange,提问作者Zlatan Radovanovic
相关产品推荐
相关产品推荐

