引言
本文中用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;
}