目录
job8/pc.c: 使用条件变量解决生产者、计算者、消费者问题
job8/pp.c: 使用条件变量实现 ping-pong 问题
job8/pc.c: 使用条件变量解决生产者、计算者、消费者问题
+ 系统中有3个线程:生产者、计算者、消费者 + 系统中有2个容量为4的缓冲区:buffer1、buffer2 + 生产者 - 生产'a'、'b'、'c'、‘d'、'e'、'f'、'g'、'h'八个字符 - 放入到buffer1 - 打印生产的字符 + 计算者 - 从buffer1取出字符 - 将小写字符转换为大写字符,按照 input:OUTPUT 的格式打印 - 放入到buffer2 + 消费者 - 从buffer2取出字符 - 打印取出的字符 + 程序输出结果(实际输出结果是交织的) a b c ... a:A b:B c:C ... A B C ...
前置知识点
进程互斥
初始化互斥量:
加锁解锁:
条件变量
初始化:
等待:
唤醒线程:
思路
跟老师的模板也是如出一辙,采用buffer1和buffer2两个缓冲区,因此,mutex互斥量也需要俩,同时wait_full_buffer和wait_empty_buffer也各需要俩。
生产者线程与消费者线程需要同步
- 生产者生产ITEM_COUNT个数据
- 消费者消费ITEM_COUNT个数据
生产者生产数据过快
- 导致缓冲区为满
- 生产者需要等待条件变量wait_empty_buffer
消费者消费数据过快
- 导致缓冲区为空
- 消费者需要等待条件变量wait_full_buffer
#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int in1,in2;
int out1,out2;
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_cond_t wait_empty_buffer1;
pthread_cond_t wait_full_buffer1;
pthread_cond_t wait_empty_buffer2;
pthread_cond_t wait_full_buffer2;
#define ITEM_COUNT (CAPACITY*2)
- 使用条件变量实现生产者与消费者问题
- 存在一个共享缓冲区
- 生产者线程向共享缓冲区1写数据
- 计算者从共享缓冲区1读数据,并向共享缓冲区2写数据
- 消费者线程从共享缓冲区2读数据
- 使用长度为4的数组表示共享缓冲区
- 变量out为共享缓冲去的读指针;变量in为共享缓冲去的写指针
-
in指针和out指针相同时,缓冲区为空;in指针和out指针相邻时,缓冲区为满
-
in指针指向数组最后一项元素时
-
in+1越界
-
通过求模保证in指针处于数组边界内
-
注意我的in和out是全局变量,因此传参的时候需要传地址,否则无法改变他们的值。
int buffer_is_empty(int *in,int *out)
{
return *in==*out;
}
int buffer_is_full(int *in,int *out)
{
return (*in+1)%CAPACITY==*out;
}
- get_item获取out指针指向的元素,同时,移动out指针指向下一项
- put_item将元素放置在in指针指向的位置,同时,移动in指针指向下一项
- type区分buffer
int get_item(int *out,int type)
{
int item;
if(type==1)item=buffer1[*out];
else item=buffer2[*out];
*out=(*out+1)%CAPACITY;
return item;
}
void put_item(int item,int *in,int type)
{
if(type==1)buffer1[*in]=item;
else buffer2[*in]=item;
*in=(*in+1)%CAPACITY;
}
完成生产者线程的框架
生产者生产ITEM_COUNT个数据
- 如果buffer_is_full为真,缓冲区为满,生产者需要一个空的缓冲区。生产者等待条件变量wait_empty_buffer。
- 生产者生产一个数据,释放一个满缓冲区,唤醒等待满缓冲区的消费者。
void *produce(void *arg)
{
int i,item,type=1;
for(i=0;i<ITEM_COUNT;i++)
{
pthread_mutex_lock(&mutex1);
while(buffer_is_full(&in1,&out1))
pthread_cond_wait(&wait_empty_buffer1,&mutex1);
item='a'+i;
put_item(item,&in1,type);
printf("produce item: %c\n",item);
pthread_cond_signal(&wait_full_buffer1);
pthread_mutex_unlock(&mutex1);
}
return NULL;
}
完成计算者框架
- 前半部分参考消费者,后半部分参考生产者。、
- 需要俩互斥量,因为是两个不同的临界区。
- 小写-32是大写
void *compute(void *arg)
{
//printf("enter compute fun");
int i,item,type;
for(i=0;i<ITEM_COUNT;i++)
{
type=1;
pthread_mutex_lock(&mutex1);
while(buffer_is_empty(&in1,&out1))
pthread_cond_wait(&wait_full_buffer1,&mutex1);
item=get_item(&out1,type);
//printf(" compute item: %c:",item);
pthread_cond_signal(&wait_empty_buffer1);
pthread_mutex_unlock(&mutex1);
type=2;
pthread_mutex_lock(&mutex2);
while(buffer_is_full(&in2,&out2))
pthread_cond_wait(&wait_empty_buffer2,&mutex2);
item-=32;
put_item(item,&in2,type);
printf(" compute item: %c:%c\n",item+32,item);
pthread_cond_signal(&wait_full_buffer2);
pthread_mutex_unlock(&mutex2);
}
}
完成消费者框架
-
如果buffer_is_empty为真,缓冲区为空,消费者需要一个满的缓冲区,消费者等待条件变量wait_full_buffer。
-
消费者取走一个数据,释放一个空缓冲区,唤醒等待空缓冲区的计算者。
void *consume(void *arg)
{
int i,item,type=2;
for(i=0;i<ITEM_COUNT;i++)
{
pthread_mutex_lock(&mutex2);
while(buffer_is_empty(&in2,&out2))
pthread_cond_wait(&wait_full_buffer2,&mutex2);
item=get_item(&out2,type);
printf(" consume item: %c\n",item);
pthread_cond_signal(&wait_empty_buffer2);
pthread_mutex_unlock(&mutex2);
}
}
总体代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#define CAPACITY 4
int buffer1[CAPACITY];
int buffer2[CAPACITY];
int in1,in2;
int out1,out2;
int buffer_is_empty(int *in,int *out)
{
return *in==*out;
}
int buffer_is_full(int *in,int *out)
{
return (*in+1)%CAPACITY==*out;
}
int get_item(int *out,int type)
{
int item;
if(type==1)item=buffer1[*out];
else item=buffer2[*out];
*out=(*out+1)%CAPACITY;
return item;
}
void put_item(int item,int *in,int type)
{
if(type==1)buffer1[*in]=item;
else buffer2[*in]=item;
*in=(*in+1)%CAPACITY;
}
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
pthread_cond_t wait_empty_buffer1;
pthread_cond_t wait_full_buffer1;
pthread_cond_t wait_empty_buffer2;
pthread_cond_t wait_full_buffer2;
#define ITEM_COUNT (CAPACITY*2)
void *produce(void *arg)
{
int i,item,type=1;
for(i=0;i<ITEM_COUNT;i++)
{
pthread_mutex_lock(&mutex1);
while(buffer_is_full(&in1,&out1))
pthread_cond_wait(&wait_empty_buffer1,&mutex1);
item='a'+i;
put_item(item,&in1,type);
printf("produce item: %c\n",item);
pthread_cond_signal(&wait_full_buffer1);
pthread_mutex_unlock(&mutex1);
}
return NULL;
}
void *compute(void *arg)
{
//printf("enter compute fun");
int i,item,type;
for(i=0;i<ITEM_COUNT;i++)
{
type=1;
pthread_mutex_lock(&mutex1);
while(buffer_is_empty(&in1,&out1))
pthread_cond_wait(&wait_full_buffer1,&mutex1);
item=get_item(&out1,type);
//printf(" compute item: %c:",item);
pthread_cond_signal(&wait_empty_buffer1);
pthread_mutex_unlock(&mutex1);
type=2;
pthread_mutex_lock(&mutex2);
while(buffer_is_full(&in2,&out2))
pthread_cond_wait(&wait_empty_buffer2,&mutex2);
item-=32;
put_item(item,&in2,type);
printf(" compute item: %c:%c\n",item+32,item);
pthread_cond_signal(&wait_full_buffer2);
pthread_mutex_unlock(&mutex2);
}
}
void *consume(void *arg)
{
int i,item,type=2;
for(i=0;i<ITEM_COUNT;i++)
{
pthread_mutex_lock(&mutex2);
while(buffer_is_empty(&in2,&out2))
pthread_cond_wait(&wait_full_buffer2,&mutex2);
item=get_item(&out2,type);
printf(" consume item: %c\n",item);
pthread_cond_signal(&wait_empty_buffer2);
pthread_mutex_unlock(&mutex2);
}
}
int main()
{
pthread_t compute_tid;
pthread_t consume_tid;
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
pthread_cond_init(&wait_empty_buffer1, NULL);
pthread_cond_init(&wait_full_buffer1, NULL);
pthread_cond_init(&wait_empty_buffer2, NULL);
pthread_cond_init(&wait_full_buffer2, NULL);
pthread_create(&compute_tid, NULL, compute, NULL);
pthread_create(&consume_tid, NULL, consume, NULL);
produce(NULL);
pthread_join(consume_tid, NULL);
pthread_join(compute_tid, NULL);
return 0;
}
结果:
job8/pp.c: 使用条件变量实现 ping-pong 问题
+ 系统中有2个线程:ping 线程和 pong 线程 + ping 线程先执行 + ping 线程执行流程如下 1. 打印输出 ping 2. 等待 pong 线程输出 3. 执行第 1 步 + pong 线程执行流程如下 1. 打印输出 pong 2. 等待 ping 线程输出 3. 执行第 1 步 + 程序输出结果 ping pong ping pong ...
思路
定义p作为状态量,p=1表示ping线程运行,p=0表示pong线程运行。要满足互斥,就要设置一个互斥量,和俩等待条件变量。当p=0即pong在运行时,进行等待pong。然后输出ping,将p设为1,表示pong即将进入临界区。释放一个等待ping的缓冲区,并唤醒pong。
代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
int p=1;
#define COUNT 10
pthread_mutex_t mutex;
pthread_cond_t wait_ping;
pthread_cond_t wait_pong;
void *ping(void *arg)
{
int i=COUNT;
while(i--){
pthread_mutex_lock(&mutex);
while(p==0)
pthread_cond_wait(&wait_pong,&mutex);
printf("ping\n");
p=0;
pthread_cond_signal(&wait_ping);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *pong(void *arg)
{
int i=COUNT;
while(i--)
{
pthread_mutex_lock(&mutex);
while(p==1)
pthread_cond_wait(&wait_ping,&mutex);
printf("pong\n");
p=1;
pthread_cond_signal(&wait_pong);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t pong_tid;
pthread_mutex_init(&mutex,NULL);
pthread_cond_init(&wait_ping,NULL);
pthread_cond_init(&wait_pong,NULL);
pthread_create(&pong_tid,NULL,pong,NULL);
ping(NULL);
pthread_join(pong_tid,NULL);
return 0;
}