C++语法|C++11标准实现生产者消费者模型

简介

在多线程编程中,生产者-消费者问题是一个经典的同步问题,它涉及到两种类型的线程:生产者和消费者。生产者负责生成数据放入缓冲区,而消费者则从缓冲区中取出数据。本文将详细介绍如何使用 C++11 的新特性来实现这一模型。

问题描述

生产者-消费者问题的关键在于如何协调生产者和消费者对共享缓冲区的访问,确保当缓冲区满时生产者停止生产,缓冲区空时消费者停止消费。

单消费者、生产者

类定义:Queue

首先,定义了一个名为Queue的类,它是生产者和消费者共享的队列。该队列内部使用std::queue来存储数据,并配有std::mutex和std::condition_variable来实现线程同步。

  • put方法:当生产者生成一个新的物品时,首先会获取互斥锁。如果队列非空,生产者会等待消费者消费。然后,它会将物品放入队列,并通知等待的消费者可以开始消费了。
  • get方法:消费者通过该方法从队列中取出物品。如果队列为空,消费者将等待生产者生产新的物品。一旦消费完成,它会通知可能在等待的生产者队列已空,可以继续生产。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

class Queue {
public: 
    void put(int val) {
        lock_guard<std::mutex> guard(mtx);
        if (!que.empty()) {
            unique_lock<std::mutex> lck(mtx);
            cv.wait(lck);
        }
        que.push(val);
        cv.notify_all(); 
        cout << "生产者 生产:" << val << "号物品" << endl;
    }

    int get() {
        lock_guard<std::mutex> guard(mtx);
        while (que.empty()) {
            unique_lock<std::mutex> lck(mtx);
            cv.wait(lck);
        }
        int val = que.front();
        que.pop();
        cv.notify_all();
        cout << "消费者 消费:" << val << "号物品" << endl;
        return val;
    }

private:
    queue<int> que;
    mutex mtx;
    condition_variable cv;
};

生产者和消费者行为

生产者和消费者通过调用 Queue 类的方法来进行生产和消费,其中包括等待和通知机制来协调操作:

void producer(Queue *que) {
    for (int i = 1; i <= 10; ++i) {
        que->put(i);
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

void consumer(Queue *que) {
    for (int i = 1; i <= 10; ++i) {
        que->get();
        this_thread::sleep_for(chrono::milliseconds(100));
    }
}

主函数

主函数创建生产者和消费者线程,并等待这些线程完成操作:

int main() {
    Queue que;
    thread t1(producer, &que);
    thread t2(consumer, &que);

    t1.join();
    t2.join();
}

整体代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

std::mutex mtx;
std::condition_variable cv; //定义条件变量,做线程间的同步通信操作

class Queue {
public: 
    void put(int val) { //生产物品
        lock_guard<std::mutex> guard(mtx);
        if (!que.empty()) {
            //que不为空,生产者应该通知消费者去消费,消费完了,在继续生产
            //生产者线程应该进入#`等待状态,并且#2吧mtx释放掉
            unique_lock<std::mutex> lck(mtx);
            cv.wait(lck);
        }
        que.push(val);
        /*
        notify_one:通知另外一个线程的
        notify_all:通知其他的所有线程的
        通知其他所有的线程,我生产了一个物品,你们赶紧消费
        其他线程得到该通知,就会从等待状态=》阻塞状态=》获取互斥锁才能继续执行
        */
        cv.notify_all(); 
        cout << "生产者 生产:" << val << "号物品" << endl;
    }
    int get() {// 消费物品
        lock_guard<std::mutex> guard(mtx);
        while (que.empty())
        {
            /* 消费者线程发现que是空的,通知生产者线程先生产物品 */
            /*#1 进入等待状态 #2 把互斥锁mutex释放掉*/
            unique_lock<std::mutex> lck(mtx);
            cv.wait(lck);
        }
        int val = que.front();
        que.pop();
        cv.notify_all(); //通知其他线程我消费完了,赶紧生产吧
        cout << "消费者 消费:" << val << "号物品" << endl;
        return val;
    }
private:
    queue<int> que;
};

void producer (Queue *que) { //生产者线程
    for (int i = 1; i <= 10; ++i) {
        que->put(i);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
void consumer (Queue *que) {  //消费者线程
    for (int i = 1; i <= 10; ++i) {
        que->get();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}
int main () {
    Queue que;  
    std::thread t1(producer, &que);
    std::thread t2(consumer, &que);

    t1.join();
    t2.join();
}

多生产者消费者模型

该多生产者多消费者模型使用条件变量确保生产者在队列满时等待,消费者在队列空时等待。当条件满足(队列非满或非空)时,相应的生产者或消费者线程被唤醒。每个生产者和消费者都通过唯一的ID生成或处理不同的

改写Queue类

在多生产者多消费者的环境中,队列可能在任何时候被多个线程同时访问,因此我们需要确保队列的每个操作都是线程安全的,并且生产者不会在队列满时添加项目,消费者不会在队列空时尝试移除项目。

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

class Queue {
public:
    void put(int val) {
        unique_lock<mutex> lck(mtx);
        // 等待队列非满
        while (que.size() == capacity) {
            cout << "队列满,生产者等待" << endl;
            cv_not_full.wait(lck);
        }
        que.push(val);
        cout << "生产者 生产:" << val << "号物品" << endl;
        cv_not_empty.notify_one();  // 通知至少一个等待的消费者
    }

    int get() {
        unique_lock<mutex> lck(mtx);
        // 等待队列非空
        while (que.empty()) {
            cout << "队列空,消费者等待" << endl;
            cv_not_empty.wait(lck);
        }
        int val = que.front();
        que.pop();
        cout << "消费者 消费:" << val << "号物品" << endl;
        cv_not_full.notify_one();  // 通知至少一个等待的生产者
        return val;
    }

private:
    queue<int> que;
    mutex mtx;
    condition_variable cv_not_full, cv_not_empty;
    static const size_t capacity = 10;  // 设置队列容量限制
};

多生产者、多消费者线程

void producer(Queue* que, int id) {
    for (int i = 0; i < 10; ++i) {
        que->put(i + id * 100);  // 确保生产的数字不重复
        this_thread::sleep_for(chrono::milliseconds(100));  // 模拟生产延时
    }
}

void consumer(Queue* que, int id) {
    for (int i = 0; i < 10; ++i) {
        que->get();
        this_thread::sleep_for(chrono::milliseconds(100));  // 模拟消费延时
    }
}

主函数

在主函数中,创建多个生产者和消费者线程。

int main() {
    Queue que;
    vector<thread> producers;
    vector<thread> consumers;

    for (int i = 0; i < 5; ++i) {  // 创建5个生产者
        producers.push_back(thread(producer, &que, i));
    }

    for (int i = 0; i < 5; ++i) {  // 创建5个消费者
        consumers.push_back(thread(consumer, &que, i));
    }

    for (auto& t : producers) {
        t.join();
    }

    for (auto& t : consumers) {
        t.join();
    }
    
    return 0;
}

整体代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std;

class Queue {
public:
    void put(int val) {
        unique_lock<mutex> lck(mtx);
        // 等待队列非满
        while (que.size() == capacity) {
            cout << "队列满,生产者等待" << endl;
            cv_not_full.wait(lck);
        }
        que.push(val);
        cout << "生产者 生产:" << val << "号物品" << endl;
        cv_not_empty.notify_one();  // 通知至少一个等待的消费者
    }

    int get() {
        unique_lock<mutex> lck(mtx);
        // 等待队列非空
        while (que.empty()) {
            cout << "队列空,消费者等待" << endl;
            cv_not_empty.wait(lck);
        }
        int val = que.front();
        que.pop();
        cout << "消费者 消费:" << val << "号物品" << endl;
        cv_not_full.notify_one();  // 通知至少一个等待的生产者
        return val;
    }

private:
    queue<int> que;
    mutex mtx;
    condition_variable cv_not_full, cv_not_empty;
    static const size_t capacity = 10;  // 设置队列容量限制
};
void producer(Queue* que, int id) {
    for (int i = 0; i < 10; ++i) {
        que->put(i + id * 100);  // 确保生产的数字不重复
        this_thread::sleep_for(chrono::milliseconds(100));  // 模拟生产延时
    }
}

void consumer(Queue* que, int id) {
    for (int i = 0; i < 10; ++i) {
        que->get();
        this_thread::sleep_for(chrono::milliseconds(100));  // 模拟消费延时
    }
}
int main() {
    Queue que;
    vector<thread> producers;
    vector<thread> consumers;

    for (int i = 0; i < 5; ++i) {  // 创建5个生产者
        producers.push_back(thread(producer, &que, i));
    }

    for (int i = 0; i < 5; ++i) {  // 创建5个消费者
        consumers.push_back(thread(consumer, &que, i));
    }

    for (auto& t : producers) {
        t.join();
    }

    for (auto& t : consumers) {
        t.join();
    }
    
    return 0;
}
  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
生产者消费者模型是一种并发编程模型,用于解决生产者和消费者之间的数据交换问题。在这个模型中,生产者负责生产数据,消费者负责消费数据。 模型实现通常涉及到共享资源(如队列)和同步机制(如锁、条件变量)。生产者和消费者通过共享的队列进行通信。当队列为空时,消费者将等待,直到有数据可供消费。当队列满时,生产者将等待,直到有空间可供生产。 在你提供的引用中,生产者线程和消费者线程使用了互斥锁和条件变量来实现同步。当消费者线程获取锁后,如果队列为空,则会进入等待状态,并释放锁。此时生产者线程可以获取锁,并判断队列是否为空,如果为空,则进入等待状态并释放锁。当生产者线程生产了产品后,会通过条件变量通知消费者线程可以消费了。消费者线程收到通知后,需要获取锁才能进行消费。 总结一下生产者消费者模型的步骤: 1. 定义共享队列作为生产者和消费者之间的数据交换通道。 2. 定义互斥锁,确保同时只有一个线程能够访问共享资源(即队列)。 3. 定义条件变量,用于线程间的通信。 4. 生产者线程获取互斥锁,判断队列是否已满,如果满则等待,否则生产数据并将其放入队列。 5. 如果消费者线程获取互斥锁,判断队列是否为空,如果为空则等待,否则消费数据并从队列中移除。 6. 在适当的时候,使用条件变量通知等待中的线程继续执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值