1.题目要求
使用条件变量解决生产者、计算者、消费者问题
- 系统中有3个线程:生产者、计算者、消费者
- 系统中有2个容量为4的缓冲区:buffer1、buffer2
- 生产者生产’a’、‘b’、‘c’、‘d’、‘e’、‘f’、‘g’、'h’八个字符,放入到buffer1
- 计算者从buffer1取出字符,将小写字符转换为大写字符,放入到buffer2
- 消费者从buffer2取出字符,将其打印到屏幕上
2.解决思路
设置两个大小为4的共享缓冲区buffer1
和buffer2
,第一个为生产者和计算者共享,第二个为计算者和消费者共享。首先对两个共享缓冲区分别实现判断缓冲区为空、判断缓冲区为满、读取一个数据、写入一个数据的基本操作子函数:定义变量out
为共享缓冲区的读指针,变量in
为共享缓冲区的写指针,in
指针和out
指针相同时,缓冲区为空;in
指针和out
指针相邻时,缓冲区为满。get_item
获取out
指针指向的元素,同时,移动ou
t指针指向下一项;put_item
将元素放置在in
指针指向的位置,同时,移动in
指针指向下一项。
生产者、计算者、消费者线程的逻辑如下:
- 生产者线程生产字符,当缓冲区被写满时,将生产者线程挂起,等待缓冲区
buffer1
有空闲存储空间的时候再唤醒生产者线程,将字符写入共享缓冲区buffer1
中,并移动写指针,唤醒计算者线程。 - 计算者线程从
buffer1
中读取数据,并移动读指针,并将读取的数据写入buffer2
中,并移动写指针,当buffer1
有空闲时,唤醒生产者进程,当buffer2
被写满时唤醒消费者进程。 - 消费者进程从
buffer2
中读取字符并输出,并移动读指针,当buffer2
有空闲的时候,唤醒计算者进程。
3.代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define CAPACITY 4
#define ITEM_COUNT 8
int buffer1[CAPACITY], buffer2[CAPACITY];
int in1, in2;
int out1, out2;
pthread_mutex_t mutex1, mutex2;
pthread_cond_t wait_empty_buffer1, wait_empty_buffer2;
pthread_cond_t wait_full_buffer1, wait_full_buffer2;
// buffer1
int buffer1_is_empty() {
return in1 == out1;
}
int buffer1_is_full() {
return (in1 + 1) % CAPACITY == out1;
}
int get_item1() {
int item = buffer1[out1];
out1 = (out1 + 1) % CAPACITY;
return item;
}
void put_item1(int item) {
buffer1[in1] = item;
in1 = (in1 + 1) % CAPACITY;
}
// buffer2
int buffer2_is_empty() {
return in2 == out2;
}
int buffer2_is_full() {
return (in2 + 1) % CAPACITY == out2;
}
int get_item2() {
int item = buffer2[out2];
out2 = (out2 + 1) % CAPACITY;
return item;
}
void put_item2(int item) {
buffer2[in2] = item;
in2 = (in2 + 1) % CAPACITY;
}
// 生产者
void *produce(void *arg) {
int item, i;
for(i = 0; i < ITEM_COUNT; i++) {
pthread_mutex_lock(&mutex1);
while(buffer1_is_full())
pthread_cond_wait(&wait_empty_buffer1, &mutex1);
item = 'a' + i;
printf("produce item: %c\n", item);
put_item1(item);
pthread_cond_signal(&wait_full_buffer1);
pthread_mutex_unlock(&mutex1);
}
return NULL;
}
// 计算者
void *compute(void *arg) {
int item, i;
for(i = 0; i < ITEM_COUNT; i++) {
pthread_mutex_lock(&mutex1);
while(buffer1_is_empty())
pthread_cond_wait(&wait_full_buffer1, &mutex1);
item = get_item1();
item = item - 32;
printf("\t\tcompute item: %c\n", item);
pthread_cond_signal(&wait_empty_buffer1);
pthread_mutex_unlock(&mutex1);
pthread_mutex_lock(&mutex2);
while(buffer2_is_full())
pthread_cond_wait(&wait_empty_buffer2, &mutex2);
put_item2(item);
pthread_cond_signal(&wait_full_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
// 消费者
void *consume(void *arg) {
int item, i;
for(i = 0; i < ITEM_COUNT; i++) {
pthread_mutex_lock(&mutex2);
while(buffer2_is_empty())
pthread_cond_wait(&wait_full_buffer2, &mutex2);
item = get_item2();
printf("\t\t\t\tconsume item: %c\n", item);
pthread_cond_signal(&wait_empty_buffer2);
pthread_mutex_unlock(&mutex2);
}
return NULL;
}
int main() {
pthread_t computer_tid, consumer_tid;
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
pthread_cond_init(&wait_empty_buffer1, NULL);
pthread_cond_init(&wait_empty_buffer2, NULL);
pthread_cond_init(&wait_full_buffer1, NULL);
pthread_cond_init(&wait_full_buffer2, NULL);
pthread_create(&computer_tid, NULL, compute, NULL);
pthread_create(&consumer_tid, NULL, consume, NULL);
produce(NULL);
pthread_join(computer_tid, NULL);
pthread_join(consumer_tid, NULL);
return 0;
}
4.运行结果
$ gcc pc1.c -lpthread
$ ./a.out
produce item: a
produce item: b
produce item: c
compute item before: a
compute item after : A
compute item before: b
compute item after : B
produce item: d
produce item: e
consume item: A
compute item before: c
compute item after : C
compute item before: d
compute item after : D
compute item before: e
compute item after : E
produce item: f
produce item: g
produce item: h
consume item: B
consume item: C
consume item: D
compute item before: f
compute item after : F
compute item before: g
compute item after : G
compute item before: h
compute item after : H
consume item: E
consume item: F
consume item: G
consume item: H