Implement a C++ multithreaded program that simulates a producer-consumer scenario. The producer generates random integers and adds them to a shared buffer, while the consumer removes integers from the buffer and calculates their sum. Use mutexes and condition variables to synchronize the threads.
PLAINFORMAT
#include <iostream>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <random>

std::queue<int> buffer;
const int buffer_size = 10;

std::mutex mtx;
std::condition_variable cv_producer, cv_consumer;

bool finished = false;

void producer(int num_items) {
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution(1, 100);

    for (int i = 0; i < num_items; ++i) {
        int value = distribution(generator);
        std::unique_lock<std::mutex> lock(mtx);

        cv_producer.wait(lock, [] { return buffer.size() < buffer_size; });

        buffer.push(value);
        std::cout << "Produced: " << value << std::endl;

        lock.unlock();
        cv_consumer.notify_one();

        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }

    finished = true;
    cv_consumer.notify_all();
}

void consumer() {
    int sum = 0;

    while (!finished || !buffer.empty()) {
        std::unique_lock<std::mutex> lock(mtx);

        cv_consumer.wait(lock, [] { return !buffer.empty() || finished; });

        if (!buffer.empty()) {
            int value = buffer.front();
            buffer.pop();
            sum += value;
            std::cout << "Consumed: " << value << std::endl;
        }

        lock.unlock();
        cv_producer.notify_one();

        std::this_thread::sleep_for(std::chrono::milliseconds(50));
    }

    std::cout << "Total sum: " << sum << std::endl;
}

int main() {
    int num_items = 20;

    std::thread producer_thread(producer, num_items);
    std::thread consumer_thread(consumer);

    producer_thread.join();
    consumer_thread.join();

    return 0;
}