经典同步问题实现(一)linux操作系统--生产者-消费者问题实现

在Linux操作系统下用C实现经典同步问题:生产者—消费者,具体要求如下:
(1)一个大小为10的缓冲区,初始状态为空。
(2)2个生产者,随机等待一段时间,往缓冲区中添加数
据,若缓冲区已满,等待消费者取走数据之后再添加,重
复10次。
(3)2个消费者,随机等待一段时间,从缓冲区中读取数
据,若缓冲区为空,等待生产者添加数据之后再读取,重
复10次。

 使用POSIX信号量中的无名信号量+条件变量来处理这个问题:

1)生产者进程和消费者对缓冲区的操作是互斥,即当前只能有一个进程对这个缓冲区进行操作,此外,生产者和生产者对缓冲区的修改也是互斥的,消费者和消费者对缓冲区的修改也是互斥的。用无名信号量来保护对缓冲区的修改。

2)生产者进入操作缓冲区之前,先要看缓冲区是否已满,如果缓冲区已满,则它必须等待消费者进程将数据取出才能写入数据,同样的,消费者进程从缓冲区读取数据之前,也要判断缓冲区是否为空,如果为空,则必须等待生产者进程写入数据才能读取数据。使用条件变量来控制。

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <time.h>
#define buffersize 10

/*信号量*/
//空的信号量和满的信号量
sem_t empty_sem, full_sem; //empty_sem初值为buffersize full_sem初值为0 
//静态创建条件变量
pthread_cond_t full = PTHREAD_COND_INITIALIZER;        //满的变量
pthread_cond_t empty = PTHREAD_COND_INITIALIZER;       //空的变量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;      //互斥锁

//定义缓冲区
struct BUFFER
{
    char product[buffersize];
    int num;//缓冲区中产品的数量
    int front, rear;//头尾指针
};

//产生缓冲区中的字母
char getrandchar()
{
    int temp = rand()%26;
    return ('A'+temp);
}

//打印当前时间
void print_time()
{
  struct tm *ptr;
  time_t lt;
  lt =time(NULL);
  printf(ctime(&lt));
}

//生产者1
void *producer1(void *agr)
{
    struct BUFFER *q;
    q = (struct BUFFER *) agr;
    while(1)//注意在linux里面不能用while(true)
    {
        //先加互斥锁
        pthread_mutex_lock(&lock);
        //判断缓冲区是否为满
        while(q->num == buffersize)
        {
            //满的话,生产者要在full这个条件变量上阻塞
            pthread_cond_wait(&full, &lock);
        }
        //非满的话,生产者可以对缓冲区进行修改,修改的时候用信号量empty_sem--
        sem_wait(&empty_sem);
        printf("当前执行的进程为:生产者1\n");
        //生产产品
        char c = getrandchar();
        q->rear = (q->rear+1)%buffersize;//生产的产品放最后面
        q->product[q->rear] = c;
        q->num++;
        printf("生产的数据为:%c\n当前缓冲区中的数据个数为:\n", c);
        printf("%d\n这些数据为:\n", q->num);
        //遍历缓冲区中的数据
        int i;
        if(q->front<q->rear)
        {
            for(i=q->front; i<=q->rear; i++)
            {
                printf("%c ", q->product[i]);
            }
            printf("\n");
        }
        else
        {
            for(i=q->front; i<buffersize; i++)
            printf("%c ", q->product[i]);
            for(i=0; i<=q->rear; i++)
            printf("%c ", q->product[i]);
            printf("\n");
        }
        print_time();
        sem_post(&full_sem);
        if(q->num == 1)//生产了一个产品就可以叫消费者来取
        {
            pthread_cond_signal(&empty);
        }
         pthread_mutex_unlock(&lock);//解开互斥锁
         sleep(rand()%2); 
    }
}

//生产者2
void *producer2(void *agr)
{
    struct BUFFER *q;
    q = (struct BUFFER *) agr;
    while(1)
    {
        //先加互斥锁
        pthread_mutex_lock(&lock);
        //判断缓冲区是否为满
       while(q->num == buffersize)
        {
            //满的话,生产者要在full这个条件变量上阻塞
            pthread_cond_wait(&full, &lock);
        }
        //非满的话,生产者可以对缓冲区进行修改,修改的时候用信号量empty_sem--
        sem_wait(&empty_sem);
        printf("当前执行的进程为:生产者2\n");
        //生产产品
        char c = getrandchar();
        q->rear = (q->rear+1)%buffersize;//生产的产品放最前面
        q->product[q->rear] = c;
        q->num++;
        printf("生产的数据为:%c\n当前缓冲区中的数据个数为:\n", c);
        printf("%d\n当前缓冲区中的数据为:\n", q->num);
        //遍历缓冲区中的数据
        int i;
        if(q->front<q->rear)
        {
            for(i=q->front; i<=q->rear; i++)
            {
                printf("%c ", q->product[i]);
            }
            printf("\n");
        }
        else
        {
            for(i=q->front; i<buffersize; i++)
            printf("%c ", q->product[i]);
            for(i=0; i<=q->rear; i++)
            printf("%c ", q->product[i]);
            printf("\n");
        }
        print_time();
        sem_post(&full_sem);
        if(q->num == 1)//生产了一个产品就可以叫消费者来取
        {
            pthread_cond_signal(&empty);
        }
         pthread_mutex_unlock(&lock);//解开互斥锁
         sleep(rand()%2); 
    }
}

//消费者1
void *consumer1(void *agr)
{
    struct BUFFER *q;
    q = (struct BUFFER *) agr;
    while(1)
    {
        //先加互斥锁
        pthread_mutex_lock(&lock);
        //判断缓冲区是否为空
        while(q->num == 0)
        {
            //若为空,消费者阻塞在条件变量empty上
           pthread_cond_wait(&empty, &lock); 
        }
        //非空
        sem_wait(&full_sem);
        printf("当前执行的进程为:消费者1\n");
        //对缓冲区进行操作
        q->front = (q->front+1)%buffersize;//从尾部找一个消费掉
        char c = q->product[q->front];
        q->product[q->front] = ' ';
        q->num--;
        
        printf("消费掉的数据为:%c\n当前缓冲区中的数据个数为:\n", c);
        printf("%d\n当前缓冲区中的数据为:\n", q->num);
        //打印缓冲区中的数据
        int i;
        if(q->front<q->rear)
        {
            for(i=q->front; i<=q->rear; i++)
            {
                printf("%c ", q->product[i]);
            }
            printf("\n");
        }
        else
        {
            for(i=q->front; i<buffersize; i++)
            printf("%c ", q->product[i]);
            for(i=0; i<=q->rear; i++)
            printf("%c ", q->product[i]);
            printf("\n");
        }
        print_time();
        sem_post(&empty_sem);
        if(q->num == buffersize-1)//消费者消费掉一个产品,就可以唤醒生产者了
        {
            pthread_cond_signal(&full);
        }
        //解开互斥锁
        pthread_mutex_unlock(&lock);
        sleep(rand()%2);
    }
    
}

//消费者2
void *consumer2(void *agr)
{
    struct BUFFER *q;
    q = (struct BUFFER *) agr;
    while(1)
    {
        //先加互斥锁
        pthread_mutex_lock(&lock);
        //判断缓冲区是否为空
        while(q->num == 0)
        {
            //若为空,消费者阻塞在条件变量empty上
           pthread_cond_wait(&empty, &lock); 
        }
        //非空
        sem_wait(&full_sem);
        printf("当前执行的进程为:消费者2\n");
        //对缓冲区进行操作
        q->front = (q->front+1)%buffersize;//从尾部找一个消费掉
        char c = q->product[q->front];
        q->product[q->front] = ' ';
        q->num--;
        
        printf("消费掉的数据为:%c\n当前缓冲区中的数据个数为:\n", c);
        printf("%d\n当前缓冲区中的数据为:\n", q->num);
        //打印缓冲区中的数据
        int i;
        if(q->front<q->rear)
        {
            for(i=q->front; i<=q->rear; i++)
            {
                printf("%c ", q->product[i]);
            }
            printf("\n");
        }
        else
        {
            for(i=q->front; i<buffersize; i++)
            printf("%c ", q->product[i]);
            for(i=0; i<=q->rear; i++)
            printf("%c ", q->product[i]);
            printf("\n");
        }
        print_time();
        sem_post(&empty_sem);
        if(q->num == buffersize-1)//消费者消费掉一个产品,就可以唤醒生产者了
        {
            pthread_cond_signal(&full);
        }
        //解开互斥锁
        pthread_mutex_unlock(&lock);
        sleep(rand()%2);
    }
    
}

int main(int argc, char* argv[])
{
    printf("生产者-消费者问题解决如下:\n");
    //为缓冲区申请空间
    struct BUFFER *q;
    q = (struct BUFFER *)malloc(sizeof(struct BUFFER));
    //初始化循环队列
    q->front = q->rear =  buffersize-1;
    q->num = 0;
    //定义四个线程
    pthread_t pid1, chil1, pid2, chil2;

    //初始化信号量
    sem_init(&empty_sem, 0, buffersize);
    sem_init(&full_sem, 0, 0);
    //创建线程
    pthread_create(&pid1, NULL, producer1, (void *) q);
    pthread_create(&chil1, NULL, consumer1, (void *) q);
    pthread_create(&pid2, NULL, producer2, (void *) q);
    pthread_create(&chil2, NULL, consumer2, (void *) q); 

    //销毁线程
    pthread_join(pid1, NULL);
    pthread_join(chil1, NULL);
    pthread_join(pid2, NULL);
    pthread_join(chil2, NULL);
    //销毁信号量
    sem_destroy(&empty_sem);
    sem_destroy(&full_sem);
       return 0;
}

需要注意的是对信号量的初始化一定要在线程创建之前。不要把顺序搞反了。

运行结果:它好像不会停下来。。。在缓冲区数据为0的时候也不会停下来
Fri Nov 30 19:16:03 2018
当前执行的进程为:消费者2
消费掉的数据为:P
当前缓冲区中的数据个数为:
1
当前缓冲区中的数据为:
  U 
Fri Nov 30 19:16:03 2018
当前执行的进程为:消费者2
消费掉的数据为:U
当前缓冲区中的数据个数为:
0
当前缓冲区中的数据为:
                      
Fri Nov 30 19:16:04 2018
当前执行的进程为:生产者1
生产的数据为:H
当前缓冲区中的数据个数为:
1
这些数据为:
  H 
Fri Nov 30 19:16:04 2018
当前执行的进程为:生产者1
生产的数据为:S
当前缓冲区中的数据个数为:
2
这些数据为:
  H S 
Fri Nov 30 19:16:04 2018
当前执行的进程为:生产者1
生产的数据为:T
当前缓冲区中的数据个数为:
3
这些数据为:
  H S T 
Fri Nov 30 19:16:04 2018
当前执行的进程为:消费者1
消费掉的数据为:H
当前缓冲区中的数据个数为:
2
当前缓冲区中的数据为:
  S T 
Fri Nov 30 19:16:04 2018
当前执行的进程为:消费者1
消费掉的数据为:S
当前缓冲区中的数据个数为:
1
当前缓冲区中的数据为:
  T 
Fri Nov 30 19:16:04 2018
当前执行的进程为:消费者1
消费掉的数据为:T
当前缓冲区中的数据个数为:
0
当前缓冲区中的数据为:
                      
Fri Nov 30 19:16:04 2018
当前执行的进程为:生产者1
生产的数据为:S
当前缓冲区中的数据个数为:
1
这些数据为:
  S 
Fri Nov 30 19:16:05 2018
当前执行的进程为:生产者1
生产的数据为:A
当前缓冲区中的数据个数为:
2
这些数据为:
  S A 
Fri Nov 30 19:16:05 2018
当前执行的进程为:生产者1
生产的数据为:B
当前缓冲区中的数据个数为:
3
这些数据为:
  S A B 
Fri Nov 30 19:16:05 2018
当前执行的进程为:消费者2
消费掉的数据为:S
当前缓冲区中的数据个数为:
2
当前缓冲区中的数据为:
  A B 
Fri Nov 30 19:16:05 2018
当前执行的进程为:消费者1
消费掉的数据为:A
当前缓冲区中的数据个数为:
1
当前缓冲区中的数据为:
  B 
Fri Nov 30 19:16:05 2018
当前执行的进程为:消费者1
消费掉的数据为:B
当前缓冲区中的数据个数为:
0
当前缓冲区中的数据为:
                      
Fri Nov 30 19:16:05 2018
当前执行的进程为:生产者1
生产的数据为:M
当前缓冲区中的数据个数为:
1
这些数据为:
  M 
Fri Nov 30 19:16:06 2018
当前执行的进程为:生产者1
生产的数据为:G
当前缓冲区中的数据个数为:
2
这些数据为:
  M G 
Fri Nov 30 19:16:06 2018
当前执行的进程为:生产者1
生产的数据为:B
当前缓冲区中的数据个数为:
3
这些数据为:
  M G B 
Fri Nov 30 19:16:06 2018
当前执行的进程为:消费者2
消费掉的数据为:M
当前缓冲区中的数据个数为:
2
当前缓冲区中的数据为:
  G B 
Fri Nov 30 19:16:06 2018

 

  • 8
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值