在模块化编程中,高内聚和低耦合通常是我们要达到的标准,相信对于生产者消费者就是一个这样的问题。
在对线程的学习中,看了一下这个实例,因此在这里学习一下。
生产者和消费者问题是一个经典的线程同步的问题,在实际生活中我们很容易遇到,也称为有限缓冲区问题,生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者
上面是百度百科对于这个问题的解释,而对于我自己的理解呢,就是一个线程往缓冲区里面写东西,另一个线程从缓冲区里面读东西,当缓冲区满的时候,我不再写,此时写线程进入休眠,等待缓冲区非满的信号过来,我们再进行写,当缓冲区空的时候,我们的读线程就进入休眠,等待缓冲区非空的信号过来,我们再进行读,因为线程同步的问题,因此,存在着多个读写线程的竞争,也就是这些线程进行着互斥访问,而且读写的速度也不一样,因此需要我们的互斥锁来锁定多线程,让线程一个一个执行
那么问题来了,一个简单的读写操作为什么要搞这么多东东呢?
解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。生产者直接调用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。
使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。
3.缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。
对于这些基本的概念,就讲这么多,下面给出思路以及我们的程序:
生产者进程:
1、调用pthread_mutex_lock()对lock上锁,并根据以下条件判断缓冲区是否已满;
(writepos + 1) % BUFSIZE == readpos
2、若满,调用ptread_cond_wait()进入阻塞,等待notfull条件变量;
3、写入数据并移动写指针writepos;
4、调用pthread_cond_signal()向消费者信号通过notempty条件变量;
5、调用pthread_mutex_unlock()对mutex解锁。
消费者进程:
1、调用pthread_mutex_lock()对lock上锁,并根据以下条件判断缓冲区是否为空;
writepos == readpos
2、若空,调用ptread_cond_wait()进入阻塞,等待notempty条件变量;
3、读取数据并移动读指针readpos;
4、调用pthread_cond_signal()向消费者信号通过notfull条件变量;
5、调用pthread_mutex_unlock()对mutex解锁。
线程管理相关函数
int pthread_create( );
int pthread_join();
线程互斥控制相关函数
int pthread_mutex_init();
int pthread_mutex_lock();
int pthread_mutex_unlock();
线程条件变量控制相关函数
int pthread_cond_init();
int pthread_cond_wait();
int pthread_cond_signal()
/*********************************************************************************
* Copyright: (C) 2017 tangyanjun<519656780@qq.com>
* All rights reserved.
*
* Filename: pro_Cus.c
* Description: This file
*
* Version: 1.0.0(08/13/2017)
* Author: tangyanjun <519656780@qq.com>
* ChangeLog: 1, Release initial version on "08/13/2017 08:35:20 PM"
*
********************************************************************************/
#include <stdio.h>
#include <pthread.h>
#define BUFSIZE 1000
#define OVER (-1)
struct producers{
int buffer[BUFSIZE];
pthread_mutex_t lock; //互斥锁
int readpos;
int writepos;
pthread_cond_t notempty; //缓冲区非空条件判断
pthread_cond_t notfull; //缓冲区未满条件判断
};
struct producers buffer;
void init(struct producers *b)
{
pthread_mutex_init(&b->lock, NULL);
pthread_cond_init(&b->notempty, NULL);
pthread_cond_init(&b->notfull, NULL);
b->readpos = 0;
b->writepos = 0;
}
void put(struct producers* b, int data)
{
pthread_mutex_lock(&b->lock); //如果互斥锁mutex已经被上锁则挂起生产者线程,并返回
if((b->writepos + 1) % BUFSIZE == b->readpos) //等待缓冲区满
{
pthread_cond_wait(&b->notfull, &b->lock); //缓冲区满,生产者将被挂起,直至重新被唤醒
}
b->buffer[b->writepos] = data; //写数据
b->writepos++; //移动指针
if(b->writepos >= BUFSIZE)
{
b->writepos = 0;
}
pthread_cond_signal(&b->notempty); //设置缓冲区非空的条件变量
pthread_mutex_unlock(&b->lock);
}
int get(struct producers *b)
{
int data;
pthread_mutex_lock(&b->lock);
if(b->writepos == b->readpos)
{
pthread_cond_wait(&b->notempty, &b->lock); //等待缓冲区非空
}
data = b->buffer[b->readpos]; //读数据
b->readpos++; //移动指针
if(b->readpos >= BUFSIZE)
{
b->readpos = 0;
}
pthread_cond_signal(&b->notfull); //设置缓冲区未满的条件变量
pthread_mutex_unlock(&b->lock);
return data;
}
void *producer(void *data)
{
int n;
for(n = 0; n < 14; n++)
{
printf("[%d] ", n);
put(&buffer, n);
}
put(&buffer, OVER);
return NULL;
}
void *consumer(void *data)
{
int d;
while(1)
{
d = get(&buffer);
if(d == OVER)
{
break;
}
printf("(%d) ", d);
}
return NULL;
}
int main()
{
int i = 0;
pthread_t th_a, th_b, th_c, th_d;
void *retval;
init(&buffer);
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
pthread_create(&th_a, NULL, producer, 0);
pthread_create(&th_b, NULL, consumer, 0);
pthread_join(th_a, &retval);
pthread_join(th_b, &retval);
printf("\n");
return 0;
}
根据我们的结果打印可以看出生产者和消费者两个线程的确是在交叉进行,竞争资源。由于时间原因,现阶段没有深入学习,以后再好好深入。