微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

生产者消费者模型多线程队列初学者个人理解

"生产者和消费者"模式 涉及到的问题: 
        
        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;//这一步很重要,将其置空可以避免在结束后仍占有资源的问题
}//在程序完成后,销毁多线程队列,释放其占有的空间

最后,带有验证属性的main.c文件

#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;
}

最后,本人还是新手刚入门学习,如果有错误的地方或者说的不够仔细地地方,希望大家可以多多包容,可以在评论区批评指正,让我可以更多的提升自身的知识水平和理解能力。

原文地址:https://www.jb51.cc/wenti/3283381.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐