C++并发学习三:生产者-消费者模型(有界缓冲区)

要求:实现一个有Capacity的Buffer,并用生产者消费者同时Push/Pop

1. 失败尝试

第一次实现的时候忘记notify_one(),像下面这样

    void push(T item) {
        std::unique_lock<std::mutex> lock(mtx_);
        not_full_cv_.wait(lock, [&]{ return queue_.size() < capacity_; });
        queue_.push(item);
        std::cout << "[Producer] queue size = " << queue_.size() << std::endl;

        // not_empty_cv_.notify_one(); <---- 忘记写了
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mtx_);
        not_empty_cv_.wait(lock, [&]{ return !queue_.empty(); });
        T res = queue_.front();
        queue_.pop();

        // not_full_cv_.notify_one();

        return res;
    }

没有notify的后果:Producer在写满capacity_后就卡住了

[Producer] Pushing 0
[Producer] queue size = 1
[Consumer] Popped 0
[Producer] Pushing 1
[Producer] queue size = 1
[Producer] Pushing 2
[Producer] queue size = 2
[Producer] Pushing 3
[Producer] queue size = 3
[Consumer] Popped 1
[Producer] Pushing 4
[Producer] queue size = 3
[Producer] Pushing 5
[Producer] queue size = 4
[Producer] Pushing 6
[Producer] queue size = 5
[Producer] Pushing 7
[Consumer] Popped 2
[Consumer] Popped 3
[Consumer] Popped 4
[Consumer] Popped 5
[Consumer] Popped 6
...无限等待没有继续打印...

2. 正确答案

加上notify_one()后成功跑通,贴下完整代码:

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <vector>

template <typename T>
class BoundedBuffer {
public:
    BoundedBuffer(size_t size) : capacity_(size) {}

    void push(T item) {
        std::unique_lock<std::mutex> lock(mtx_);
        not_full_cv_.wait(lock, [&]{ return queue_.size() < capacity_; });
        queue_.push(item);
        std::cout << "[Producer] queue size = " << queue_.size() << std::endl;

        not_empty_cv_.notify_one();
    }

    T pop() {
        std::unique_lock<std::mutex> lock(mtx_);
        not_empty_cv_.wait(lock, [&]{ return !queue_.empty(); });
        T res = queue_.front();
        queue_.pop();

        not_full_cv_.notify_one();

        return res;
    }

private:
    size_t capacity_;
    std::queue<T> queue_;

    std::mutex mtx_;
    // 当缓冲区不满时,通知生产者
    std::condition_variable not_full_cv_;
    // 当缓冲区不空时,通知消费者
    std::condition_variable not_empty_cv_;
};

int main() {
    BoundedBuffer<int> buffer(5);

    std::thread producer([&]() {
        for (int i=0; i<10; i++) {
            std::cout << "[Producer] Pushing " << i << std::endl;
            buffer.push(i);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
    });

    std::thread consumer([&]() {
        for (int i=0; i<10; i++) {
            int item = buffer.pop();
            std::cout << "[Consumer] Popped " << item << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(400));
        }
    });

    producer.join();
    consumer.join();

    return 0;
};

运行成功

[Producer] Pushing 0
[Producer] queue size = 1
[Consumer] Popped 0
[Producer] Pushing 1
[Producer] queue size = 1
[Producer] Pushing 2
[Producer] queue size = 2
[Producer] Pushing 3
[Producer] queue size = 3
[Consumer] Popped 1
[Producer] Pushing 4
[Producer] queue size = 3
[Producer] Pushing 5
[Producer] queue size = 4
[Producer] Pushing 6
[Producer] queue size = 5
[Producer] Pushing 7
[Consumer] Popped 2
[Producer] queue size = 5
[Producer] Pushing 8
[Consumer] Popped 3
[Producer] queue size = 5
[Producer] Pushing 9
[Consumer] Popped 4
[Producer] queue size = 5
[Consumer] Popped 5
[Consumer] Popped 6
[Consumer] Popped 7
[Consumer] Popped 8
[Consumer] Popped 9

3. 原因解释

下面我通过push()的运行流程来解释为什么不进行notify会导致worker无响应:

  1. std::unique_lock<std::mutex> lock(mtx_);获取到了锁mtx_
  2. not_full_cv_.wait(lock, [&]{ return queue_.size() < capacity_; });的时候,std::condition_variabl会检测condition是否满足:
    • 如果满足:则继续执行接下来的代码(直到出作用域后,std::unique_lock会自动把锁归还)
    • 如果不满足:则立刻原子地进行下面两件事情:{把取得的锁归还, 阻塞当前线程}

因此,若条件queue_.size() < capacity不满足,则worker线程会立刻被阻塞,此后除非被假性唤醒,不然理论上该线程不会被唤醒,直到被其他线程notify。故而,不进行notify会导致worker无响应。

posted @ 2025-11-12 13:49  KBZ232  阅读(15)  评论(0)    收藏  举报