linux下C语言实现生产者-消费者问题实现
整合代码: 整个程序代码
使用信号量解决有限缓冲区生产者/消费者问题(伪代码)
producer : consumer:
while(true){ while(true){
/*生产 v*/ while(in == out)(不做任何事);
while((in + 1)% n == out)(不做任何事); w = b[out];
b[in] = v; out = (out + 1) % n;
in = (in + 1) % n; /*消费 w*/
} }
const int sizeofbuffer = /*缓冲区大小*/
semaphore s = 1, n = 0, e = sizeofbuffer;
void producer() void consumer()
{ {
while(true){ while(true){
produce(); /*生产*/ semWait(n); /*空不取*/
semWait(e); /*满不放*/ semWait(s); /*互斥信号量*/
semWait(s); /*互斥信号量*/ take();/*w = b[out];
out = (out + 1) % n;*/
append(); /*b[in] = v;*/ semSignal(s);
/*in = (in + 1) % n;*/ semSingal(e);
semSignal(s); consume() /*消费*/
semSingal(n); }
}
} }
void main()
{
parbegin(producer, consumer);
}
一、main()
int main()
{
pthread_t id1[producerNumber]; // 声明生产者线程的ID数组
pthread_t id2[consumerNumber]; // 声明消费者线程的ID数组
int i;
int ret1[producerNumber];
int ret2[consumerNumber];
// 初始化同步信号量
int ini1 = sem_init(&empty_sem, 0, M);
// 原型为:extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
// 第一个参数:sem为指向信号量结构的一个指针;
// 第二个参数:pshared==0确保只能为当前的进程的所有线程共享
// 第三个参数:value为信号量的初始值
int ini2 = sem_init(&full_sem, 0, 0); // 同上初始化的描述
if(ini1 && ini2 != 0) //初始化失败
{
printf("sem init failed \n");
exit(1);
}
//初始化互斥信号量的函数pthread_mutex_init();
int ini3 = pthread_mutex_init(&mutex, NULL);
// 抽象的锁类型的结构;
//函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 以动态方式创建互斥锁
// 第一个参数:为指向互斥信号量结构的一个指针
// 第二个参数: 指定了新建互斥锁的属性,NULL表明使用默认的互斥锁属性(快速互斥锁)
if(ini3 != 0)
{
printf("mutex init failed \n");
exit(1);
}
// 创建producerNumber个生产者线程
for(i = 0; i < producerNumber; i++)
{
ret1[i] = pthread_create(&id1[i], NULL, product, NULL);
// 用ret1[]数组记录是否创建线程成功,因为pthread_create()函数返回值为int类型;若返回值为0表示创建成功,否则返回出错的编号
// 函数原型:int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
// 第一个参数:为指向线程标识符(ID号)的指针
// 第二个参数: 用来设置线程的属性
// 第三个参数: 线程运行函数的地址
// 第四个参数: 运行函数的参数
if(ret1[i] != 0)
{
printf("product%d creation failed \n", i);
exit(1);
}
}
//创建consumerNumber个消费者线程
for(i = 0; i < consumerNumber; i++)
{
ret2[i] = pthread_create(&id2[i], NULL, prochase, NULL);
if(ret2[i] != 0)
{
printf("prochase%d creation failed \n", i);
exit(1);
}
}
//销毁线程
for(i = 0; i < producerNumber; i++)
{
pthread_join(id1[i],NULL);
//pthread_join()函数来使主线程阻塞以等待其他线程退出
}
for(i = 0; i < consumerNumber; i++)
{
pthread_join(id2[i],NULL);
}
exit(0);
}
二、使用步骤
1.引入库
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //windows下用windows.h
#include <pthread.h> // 实现多线程的头文件
#include <semaphore.h> // 实现信号量定义的头文件
#define producerNumber 3 // 生产者的数目
#define consumerNumber 4 // 消费者的数目
#define M 6 // 缓冲区数目
int in = 0; // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
int buff[M] = {0}; // 缓冲初始化为0, 开始时没有产品
sem_t empty_sem; // 信号量的数据类型为结构sem_t,它本质上是一个长整型的数,同步信号量, 当满了时阻止生产者放产品
sem_t full_sem; // 同步信号量, 当没产品时阻止消费者消费
pthread_mutex_t mutex; // 互斥信号量, 一次只有一个线程访问缓冲
int producer_id = 0; // 生产者id
int consumer_id = 0; // 消费者id
2.创建生产者/消费者的函数
代码如下):
/* 打印缓冲情况 */
void print()
{
int i;
for(i = 0; i < M; i++)
printf("%d ", buff[i]);
printf("\n");
}
/* 生产者方法 */
void *product()
{
int id = ++producer_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(&empty_sem); // 满不放;书本上的semWait(e=M(缓冲区大小))操作;
pthread_mutex_lock(&mutex); // 实现互斥
in = in % M;
buff[in] = rand()%10;
printf("生产者%d向%d号缓冲区放入了一个数据%d: \t", id, in, buff[in]);
print();
++in;
pthread_mutex_unlock(&mutex);
sem_post(&full_sem);
}
}
/* 消费者方法 */
void *prochase()
{
int id = ++consumer_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(&full_sem); // 空不取
pthread_mutex_lock(&mutex); // 实现互斥
out = out % M;
printf("消费者%d从%d号缓冲区取出一个数据%d: \t", id, out, buff[out]);
buff[out] = 0;
print();
++out;
pthread_mutex_unlock(&mutex);
sem_post(&empty_sem);
}
}
总结(实现代码)
用到了<pthread.h>和<semaphore.h>两个头文件内函数,本来还以为会很难,但查找资料的时候从别人哪里看到了这两种头文件就发现其实没有想象的那么难。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> //windows下用windows.h
#include <pthread.h> // 实现多线程的头文件
#include <semaphore.h> // 实现信号量定义的头文件
#define producerNumber 3 // 生产者的数目
#define consumerNumber 4 // 消费者的数目
#define M 6 // 缓冲区数目
int in = 0; // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
int buff[M] = {0}; // 缓冲初始化为0, 开始时没有产品
sem_t empty_sem; // 信号量的数据类型为结构sem_t,它本质上是一个长整型的数,同步信号量, 当满了时阻止生产者放产品
sem_t full_sem; // 同步信号量, 当没产品时阻止消费者消费
pthread_mutex_t mutex; // 互斥信号量, 一次只有一个线程访问缓冲
int producer_id = 0; // 生产者id
int consumer_id = 0; // 消费者id
/* 打印缓冲情况 */
void print()
{
int i;
for(i = 0; i < M; i++)
printf("%d ", buff[i]);
printf("\n");
}
/* 生产者方法 */
void *product()
{
int id = ++producer_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(&empty_sem); // 满不放;书本上的semWait(e=M(缓冲区大小))操作;
pthread_mutex_lock(&mutex); // 实现互斥
in = in % M;
buff[in] = rand()%10;
printf("生产者%d向%d号缓冲区放入了一个数据%d: \t", id, in, buff[in]);
print();
++in;
pthread_mutex_unlock(&mutex);
sem_post(&full_sem);
}
}
/* 消费者方法 */
void *prochase()
{
int id = ++consumer_id;
while(1)
{
// 用sleep的数量可以调节生产和消费的速度,便于观察
sleep(1);
//sleep(1);
sem_wait(&full_sem); // 空不取
pthread_mutex_lock(&mutex); // 实现互斥
out = out % M;
printf("消费者%d从%d号缓冲区取出一个数据%d: \t", id, out, buff[out]);
buff[out] = 0;
print();
++out;
pthread_mutex_unlock(&mutex);
sem_post(&empty_sem);
}
}
int main()
{
pthread_t id1[producerNumber]; // 声明生产者线程的ID数组
pthread_t id2[consumerNumber]; // 声明消费者线程的ID数组
int i;
int ret1[producerNumber];
int ret2[consumerNumber];
// 初始化同步信号量
int ini1 = sem_init(&empty_sem, 0, M);
// 原型为:extern int sem_init __P ((sem_t *__sem, int __pshared, unsigned int __value));
// 第一个参数:sem为指向信号量结构的一个指针;
// 第二个参数:pshared==0确保只能为当前的进程的所有线程共享
// 第三个参数:value为信号量的初始值
int ini2 = sem_init(&full_sem, 0, 0); // 同上初始化的描述
if(ini1 && ini2 != 0) //初始化失败
{
printf("sem init failed \n");
exit(1);
}
//初始化互斥信号量的函数pthread_mutex_init();
int ini3 = pthread_mutex_init(&mutex, NULL);
// 抽象的锁类型的结构;
//函数原型:int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); 以动态方式创建互斥锁
// 第一个参数:为指向互斥信号量结构的一个指针
// 第二个参数: 指定了新建互斥锁的属性,NULL表明使用默认的互斥锁属性(快速互斥锁)
if(ini3 != 0)
{
printf("mutex init failed \n");
exit(1);
}
// 创建producerNumber个生产者线程
for(i = 0; i < producerNumber; i++)
{
ret1[i] = pthread_create(&id1[i], NULL, product, NULL);
// 用ret1[]数组记录是否创建线程成功,因为pthread_create()函数返回值为int类型;若返回值为0表示创建成功,否则返回出错的编号
// 函数原型:int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);
// 第一个参数:为指向线程标识符(ID号)的指针
// 第二个参数: 用来设置线程的属性
// 第三个参数: 线程运行函数的地址
// 第四个参数: 运行函数的参数
if(ret1[i] != 0)
{
printf("product%d creation failed \n", i);
exit(1);
}
}
//创建consumerNumber个消费者线程
for(i = 0; i < consumerNumber; i++)
{
ret2[i] = pthread_create(&id2[i], NULL, prochase, NULL);
if(ret2[i] != 0)
{
printf("prochase%d creation failed \n", i);
exit(1);
}
}
//销毁线程
for(i = 0; i < producerNumber; i++)
{
pthread_join(id1[i],NULL);
//pthread_join()函数来使主线程阻塞以等待其他线程退出
}
for(i = 0; i < consumerNumber; i++)
{
pthread_join(id2[i],NULL);
}
exit(0);
}
pthread_create、pthread_join、pthread_mutex_init 、pthread_mutex_lock、pthread_mutex_unlock用法from:函数的使用
调试截图: