文章目录
此文章参考b站视频 BV1Xk4y1q7RK
问题描述
在同一个进程地址空间内执行两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻挡,直到新的物品被生产出来。
分析
1.不能向满存
2.不能向空取
3.同一时间只能有一个操作
设缓冲区编号0~N buffer[N];
设三个信号量如下:
full:0
empty:N
mutex:互斥信号量,初值为1,表示各进程互斥进入临界区,保证任何时候只有一个进程使用缓冲区
P-V+
过程
int in = 0,out = 0;
生产者进程Producer: 消费者进程Consumer:
//使用缓冲区(mutex)时,PV保护
P(empty);/*有了空位才能往里放*/ P(full); /*有了才能取*/
P(mutex); P(mutex);
while(1){
while(1){
产品送往buffer[in]; 从buffer[out]中取出产品;
in = (in + 1)mod N; out = (out + 1)mod N;
/*以N为模*/ /*以N为模*/
V(mutex); V(mutex);
V(full); V(empty);
/*生产了会给消费者一个*/ /*消费者使用了会留下一个空位*/
} }
问:
1.生产者进程中 P(empty) 以及 P(mutex) 能不能交换次序?
答:不能,会造成死锁。
假设现在进展到缓冲区已装满,将 P(mutex) 放在上面,表示抢到了使用缓冲区的权利,这时消费者进程进来,P(full) 可以正常执行,占用一个生产者产生的数据,但P(mutex) 无法执行,也就无法释放那个被占用的空位V(empty).
2.生产者进程中的两个V操作能否交换顺序?
答:可以交换,V(mutex)放在上面可以减小临界区
用AND信号解决生产者-消费者问题
具体:
用Swait(empty,mutex)来代替P(emptye) 和 P(mutex);//放在一起就不用担心死锁问题
用Ssignal(mutex,full)来代替V(mutex) 和 V(full);
用Swait(full,mutex)来代替P(full) 和 P(mutex);
用Ssignal(mutex,empty)来代替V(mutex) 和 V(empty);
int in = 0,out = 0;
item buffer[n];
semaphore mutex = 1,empty = n,full = 0;
void producer(){
void consumer(){
do{
do{
producer an item nextp; Swait(full,mutex);
...... nextc = buffer[out];
Swait(empty,mutex); out = (out+1)%n;
buffer[in] = nextp; Ssignal(mutex,empty);
in = (in + 1)%n; consumer the item in nextc;
Ssignal(mutex,full); ......
}while(1); }while(1);
} }
代码实现
#include <stdio.h>
#include <pthread.h>
#include <windows.h>
#define N 100
#define true 1
#define producerNum 10
#define consumerNum 5
#define sleepTime 1000
typedef int semaphore;
typedef int item;
item buffer[N] = {
0};
int in = 0;
int out = 0;
int proCount = 0;
semaphore mutex = 1, empty = N, full = 0, proCmutex = 1;
void * producer(void * a){
while(true){
while(proCmutex <= 0);
proCmutex--;
proCount++;
printf("生产一个产品ID%d, 缓冲区位置为%d\n",proCount,in);
proCmutex++;
while(empty <= 0){
printf("缓冲区已满!\n");
}
empty--;
while(mutex <= 0);
mutex--;
buffer[in] = proCount;
in = (in + 1) % N;
mutex++;
full++;
Sleep(sleepTime);
}
}
void * consumer(void *b){
while(true){
while(full <= 0){
printf("缓冲区为空!\n");
}
full--;
while(mutex <= 0);
mutex--;
int nextc = buffer[out];
buffer[out] = 0;//消费完将缓冲区设置为0
out = (out + 1) % N;
mutex++;
empty++;
printf("\t\t\t\t消费一个产品ID%d,缓冲区位置为%d\n", nextc,out);
Sleep(sleepTime);
}
}
int main()
{
pthread_t threadPool[producerNum+consumerNum];
int i;
for(i = 0; i < producerNum; i++){
pthread_t temp;
if(pthread_create(&temp, NULL, producer, NULL) == -1){
printf("ERROR, fail to create producer%d\n", i);
exit(1);
}
threadPool[i] = temp;
}//创建生产者进程放入线程池
for(i = 0; i < consumerNum; i++){
pthread_t temp;
if(pthread_create(&temp, NULL, consumer, NULL) == -1){
printf("ERROR, fail to create consumer%d\n", i);
exit(1);
}
threadPool[i+producerNum] = temp;
}//创建消费者进程放入线程池
void * result;
for(i = 0; i < producerNum+consumerNum; i++){
if(pthread_join(threadPool[i], &result) == -1){
printf("fail to recollect\n");
exit(1);
}
}//运行线程池
return 0;
}