"生产者和消费者"模式 涉及到的问题:
1. 共享资源的互斥访问
缓冲区相当于就是一个缓冲区 多个进程/线程往里面读/取数据,
涉及到进程/线程间如果有序的访问共享资源的问题。
我们可以通过信号量/线程互斥锁解决。
2. 另外一点就是当缓冲区满,生产者不能往缓冲区写入数据 或者 当缓冲区为空 消费者就不能从缓冲区读取数据
该怎么处理?
解决方法有两种:
a.不停的测试/循环 看缓冲区里面有没有数据或者缓冲区是否有空间
这种方式我们俗称为“轮询”--》轮流询问 代码实现较为简单
对于消费者来说:
while(1)
{
if(缓冲区里面有数据)
{
//上锁
//从缓冲区读取数据
//读取数据之后处理数据
//解锁
}
}
对于生成者来说:
while(1) { if(缓冲区有空间了) { //上锁 //往缓冲区写入数据 //解锁 } }
那么这种“轮询”会有天生的缺陷
效率慢 会有时间差 不及时 浪费cpu等问题
b. 另一种方法当没有数据可以读或者没有空间可以写的时候
直接sleep(休眠 让出cpu) 当有数据或者有空间的时候 你通知我(唤醒我)
这种方式叫做线程条件变量
以下我们以条件变量的方式完成一次(生产者消费者模型创建的多线程队列)
首先要理清什么时候出队什么时候入队,在出队的时候是否要上锁,在入对的时候是否要上锁,什么时候需要让进程休眠等待,理清楚了先后顺序和逻辑后,我们可以开始写代码了。
slqueue.h文件
#ifndef SLQUEUE_H
#define SLQUEUE_H
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <semaphore.h>
#include <string.h>
#define SLQisEmpty 1
#define SLQisnotEmpty 0
typedef int Elemtype;
typedef struct slque
{
Elemtype data;
struct slque* next;
}SLqueue;
typedef struct
{
SLqueue* front;
SLqueue* rear;
int size;
}Queue;
Queue* Init_Slqueue();
int IsEmpty(Queue *slqueue);
int Get_slqueue_size(Queue* slqueue);
void Enslqueue(Queue* slqueue,Elemtype x);
Elemtype Deslqueue(Queue *slqueue);
void Clearslqueue(Queue *slqueue);
void Destroyslqueue(Queue *slqueue);
#endif
slqueue.c文件
#include "slqueue.h"
/*
************因为要创建一个全局变量的队列************
创建一个semlinkedqueue
多线程队列
作为生产者消费者模型
*/
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
Queue *Init_Slqueue() //初始化一个Sl队列
{
Queue *slqueue = (Queue *)malloc(sizeof(Queue));
slqueue->front = NULL;
slqueue->rear = NULL;
slqueue->size = 0;//初始化 使队列的头、尾指针置空,并且大小
return slqueue;
}
int IsEmpty(Queue *slqueue) //判断sl队列是否为空
{
if (slqueue->front == NULL && slqueue->rear == NULL && slqueue->size == 0)
return SLQisEmpty;
else
return SLQisnotEmpty;
}
void Enslqueue(Queue *slqueue, Elemtype x) //入队操作
{
SLqueue* sl = (SLqueue*)malloc(sizeof(SLqueue));
sl->data = x;
sl->next = NULL;
//初始化一个希望入队的结点
/*
往sl队列内写操作时 上锁
写完时 解锁并发送信号
int pthread_cond_signal(pthread_cond_t *cond);
只唤醒一个线程 唤醒处于等待队列中最前面的那个线程
返回值:
成功返回0
失败返回其他值
*/
pthread_mutex_lock(&mutex);//互斥锁
if (IsEmpty(slqueue))
slqueue->front = sl;
else
slqueue->rear->next = sl;
slqueue->rear = sl;
slqueue->size++;
//执行完入队操作 可以解开锁,并且释放信号唤醒等待队列中的线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
return;
}
Elemtype Deslqueue(Queue *slqueue) //队头元素出队
{
Elemtype res;
//对堆空间中数据进行操作的时候
//将上锁,在执行完出队操作后解锁
pthread_mutex_lock(&mutex);
while(IsEmpty(slqueue))
{
pthread_cond_wait(&cond,&mutex);//只要队列为空,一直阻塞等待直到传递信号被唤醒
}
/*printf("slqueue.front.data=%p\n",slqueue->front);
printf("slqueue.rear.data=%p\n",slqueue->rear);
printf("slqueue.size=%d\n",slqueue->size);
*/
if (slqueue->front->next == NULL)//若队列中只有一个元素
{
res=slqueue->front->data;
free(slqueue->front);
slqueue->rear = NULL;
slqueue->front = NULL;
}
else
{
SLqueue* sl = slqueue->front;
res=sl->data;
slqueue->front=slqueue->front->next;
free(sl);
}
slqueue->size--;
pthread_mutex_unlock(&mutex);//解锁
return res;//返回出队元素的值,方便确认是否正确入队出队
}
void Clearslqueue(Queue *slqueue)
{
while (!IsEmpty(slqueue))
Deslqueue(slqueue);
}
void Destroyslqueue(Queue *slqueue)
{
if(!slqueue)
return;
Clearslqueue(slqueue);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
free(slqueue);
slqueue = NULL;//这一步很重要,将其置空可以避免在结束后仍占有资源的问题
}//在程序完成后,销毁多线程队列,释放其占有的空间
#include "slqueue.h"
pthread_t producer;//生产者
pthread_t consumer;//消费者
Queue *slqueue=NULL;
void* produce(void* arg)
{
for(int i =0;i<20;i++)
{
Enslqueue(slqueue,20);
}
}//生产者线程,执行入队操作
void* consum(void*arg)
{
Elemtype res=0;
int runtimes=0;
while(!IsEmpty(slqueue))
{
res=Deslqueue(slqueue);
printf("res=%d\t",res);
runtimes++;
printf("this is the %d time runing\n",runtimes);
}
}//消费者线程,从内存中读取生产者存入的数据,并将其读出打印
int main(int argc, char const *argv[])
{
slqueue=Init_Slqueue();
pthread_create(&producer, NULL, produce,NULL);
pthread_create(&consumer, NULL, consum,NULL);
pthread_join(producer, NULL);
pthread_join(consumer, NULL);//等待两个线程结束并释放其占有的资源
Clearslqueue(slqueue);
Destroyslqueue(slqueue);//收尾工作
return 0;
}
最后,本人还是新手刚入门学习,如果有错误的地方或者说的不够仔细地地方,希望大家可以多多包容,可以在评论区批评指正,让我可以更多的提升自身的知识水平和理解能力。