C语言和C++实现消费者守护者模型

引言

本文中用C++与C语言描述了消费者守护者模型 并简单描述了其中一些坑点

不清楚什么是消费者守护者模型的可以先学习一下: 传送门

C语言版

其实其中的一个容易忽略的点就是虚假唤醒 
虚假唤醒简单来说就是一次条件变量的发送触发了两次唤醒 但资源只有一份 就导致两次唤醒中就有一份无效的唤醒 从而引发不可预估的影响 有时还可能导致程序直接崩溃 
虚假唤醒详解

#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<vector>
#include<iostream>
using namespace std;

vector<int> vec;
pthread_mutex_t mutex_product; //维护生产者队列
pthread_mutex_t mutex_consume; //维护消费者队列
pthread_cond_t cond;

void* product(void *)      //生产者
{
    int count=0;
    while(1)
    {
        pthread_mutex_lock(&mutex_product);
        vec.emplace_back(count++);
        cout << "生产者进程号:" << pthread_self() << endl;
        cout << "生产者产出: " << count << endl;
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex_product);
        sleep(3);
    }
}

void* consume(void *)
{
    while(1){
        pthread_mutex_lock(&mutex_consume);
        while(vec.empty()) //防止虚假唤醒
        {
            pthread_cond_wait(&cond,&mutex_consume);//等待生产折发出信号
        }
        cout << "消费者进程号:" << pthread_self() << endl;
        cout << "消费者得到锁,数据为 :" << vec.back() << endl;
        vec.pop_back();
        pthread_mutex_unlock(&mutex_consume);
    }
}

int main()
{
    pthread_mutex_init(&mutex_consume,NULL);
    pthread_mutex_init(&mutex_product,NULL);
    pthread_cond_init(&cond,NULL);
    pthread_t pid1,pid2,pid3,pid4;

    //生产者队列
    pthread_create(&pid1,NULL,product,NULL);
    pthread_create(&pid2,NULL,product,NULL);

    //消费者队列
    pthread_create(&pid3,NULL,consume,NULL);
    pthread_create(&pid4,NULL,consume,NULL);
    
    pthread_join(pid1,NULL);
    pthread_join(pid2,NULL);
    pthread_join(pid3,NULL);
    pthread_join(pid4,NULL);

    pthread_mutex_destroy(&mutex_consume);
    pthread_mutex_destroy(&mutex_product);
    pthread_cond_destroy(&cond);
    return 0;
}

C++版

其实基本概念上的问题在写C语言时已经解决了 但在写C++时遇到了一个语法层面的坑点 就是成员函数无法作为回调函数,原因是因为成员函数会在传参数时隐式的传一个 this 指针,就导致回调函数返回时参数不同 从而引发错误 解决原因在我的另外一篇博客中:成员函数为什么不能做为回调函数

#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>
using namespace std;


//类的成员函数作为线程函数使用
class cs
{
    public:
        static cs * pthis;         //这里有一个隐性的问题 成员函数不能作为回调函数
        friend void * ddd(void *arg);
        static void * customer(void *arg);//可以申请一个静态指针 把对象实体赋给指针 记的静态成员要初始化
        static void * producer(void *arg);//原因是因为类外初始化可保证类内的等于号始终为赋值
        cs();
        cs& operator=(const cs &tmp) = default;
        cs(const cs &tmp) = default;
        ~cs();
    private:
        queue<int>que;
        void tempa(){cout << "消费者已消费!\n";}
        void tempb(){cout << "生产者已生产!\n";}
        pthread_mutex_t mux;
        pthread_cond_t  con;
        pthread_t com_ptr;
        pthread_t pro_ptr;

};

cs::~cs()
{
    pthread_cond_broadcast(NULL);
    pthread_join(com_ptr,NULL);
    pthread_join(pro_ptr,NULL);
    std::cout << "两个线程已销毁\n";
}

cs::cs():com_ptr(0),pro_ptr(0)  //赋值一定要注意 不然容易段错误
{
    pthread_mutex_init(&mux,NULL);
    pthread_cond_init(&con,NULL);
    pthread_create(&com_ptr,NULL,pthis->producer,NULL);
    pthread_create(&pro_ptr,NULL,pthis->customer,NULL);
}

void *ddd(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&(pthis->mux));
        pthis->que.push(1);
        pthis->tempb();
        pthread_cond_signal(&(pthis->con));
        pthread_mutex_unlock(&(pthis->mux));
        sleep(2);
    }
}

void * cs::producer(void *arg)
{
    while(1)
    {
        pthread_mutex_lock(&(pthis->mux));
        pthis->que.push(1);
        pthis->tempb();
        pthread_cond_signal(&(pthis->con));
        pthread_mutex_unlock(&(pthis->mux));
        sleep(2);
    }
}

void *cs::customer(void *arg)
{
    cout << "消费者\n";
    while(1)
    {
        pthread_mutex_lock(&(pthis->mux));
        while(pthis->que.empty())
        {
            pthread_cond_wait(&(pthis->con),&(pthis->mux));
        }
        if(pthis->que.empty())
        {
            cerr << "模型出现错误!\n";
            break;  //这个函数只要退出就会发生错误
        }
        pthis->que.pop();
        pthis->tempa();
        pthread_mutex_unlock(&(pthis->mux));
    }
}
cs * cs::pthis=nullptr;   //静态成员必须初始化 
int main()
{
    cs tmp;
    cs::pthis = &tmp;  //把对象本身赋值给这个静态指针 //不足就是破坏了类的封装性
    while(1)
    sleep(30);
    return 0;
}
posted @ 2022-07-02 13:18  李兆龙的博客  阅读(21)  评论(0编辑  收藏  举报