2022-2-3 牛客C++项目 —— 生产者消费者模型(条件变量、信号量)

如果生产者将容器填满,需要通知消费者,
如果消费者将容器清空,需要通知生产者。
(我个人觉得如果有多个生产者和消费者,就排成队列,通知第一个就行。不知道实际情况是什么样子的)

在这里插入图片描述

/*
生产者消费者模型(简单版)
*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
struct Node
{
    int num;
    struct Node*next;
};

//头节点
struct Node*head = NULL;

void *producer(void*arg){
    //不断的创建新的节点,添加到链表中
    while (1)
    {
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand()%1000;
        printf("add node, num : %d,tid : %ld.\n",newNode->num,pthread_self());
        usleep(100);
    }
    
    return NULL;
}
void *customer(void*arg){
    while (1)
    {
        //保存头节点的指针
        struct Node*tmp = head;
        head = head->next;
        printf("del node,num : %d,tid : %ld.\n",tmp->num,pthread_self());
        free(tmp);
        usleep(100);
    }
    
    return NULL;
}
int main(void){
    pthread_t ptids[5],ctids[5];

    for(int i = 0;i < 5;i++){
        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }
    for(int i = 0;i < 5;i++){
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }
    
    pthread_exit(NULL);

    return 0;
}

简单的运行的结果
解决方法:
1)互斥量
2)条件变量,实现线程的通信
在这里插入图片描述
如何生成 core 文件?
1、首先查看并更改 core 文件的大小设置
在这里插入图片描述
在这里插入图片描述
2、在生成的可执行文件中加入可调式信息
在生成了core 文件后,打开 gdb
在这里插入图片描述
3、通过使用 core-file (core filename)来打开 core 文件,查看 core 文件当中的内容。
在这里插入图片描述
总的来说,上面的程序存在两个问题:1)一个是多个进程操作同一个数据但是没有同步的问题
2)另一个是当 head 为null 的时候会被 costumer 访问 head-> next 的问题。

解决第一个问题:加上互斥量

在这里插入代码片

会发生死锁
在这里插入图片描述
解决第二个问题,加上条件变量,生产者生产完成后唤醒消费者,消费者在没有物品的时候等待。

但是依然会死锁,因为没有释放互斥量
在这里插入图片描述

/*
    条件变量的类型 pthread_cond_t
    int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
    int pthread_cond_destroy(pthread_cond_t *cond);
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
        - 等待,调用了该函数,线程会阻塞
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
        - 等待多长时间,调用了这个函数,线程会阻塞,直到指定的时间阻塞
    int pthread_cond_signal(pthread_cond_t *cond);
        - 唤醒一个或者多个等待的线程
    int pthread_cond_broadcast(pthread_cond_t *cond);
        - 唤醒所有的等待的线程
*/

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
//创建一个互斥量
pthread_mutex_t mutex;
//创建条件变量
pthread_cond_t cond;

struct Node
{
    int num;
    struct Node*next;
};

//头节点
struct Node*head = NULL;

void *producer(void*arg){
    //不断的创建新的节点,添加到链表中
    while (1)
    {
        pthread_mutex_lock(&mutex);
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand()%1000;
        printf("add node, num : %d,tid : %ld.\n",newNode->num,pthread_self());
        //只要生产了一个就通知消费者消费
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&mutex);
        usleep(100);
    }
    
    return NULL;
}
void *customer(void*arg){
    while (1)
    {
        //保存头节点的指针
        pthread_mutex_lock(&mutex);
      
        struct Node*tmp = head;
        //判断是否有数据
        if(head != NULL){
        head = head->next;
        printf("del node,num : %d,tid : %ld.\n",tmp->num,pthread_self());
        free(tmp);
        pthread_mutex_unlock(&mutex);
        usleep(100);
        }else{
            //没有数据,需要等待
            pthread_cond_wait(&cond,&mutex);
            //当wait 函数对这个线程进行阻塞的时候,会对这个线程进行解锁,线程不阻塞的时候,会把mutex 互斥量加上去。
     //这样就不至于产生影响。
            pthread_mutex_unlock(&mutex);
          //  pthread_mutex_unlock(&mutex);
        }
       
        
    }
    
    return NULL;
}
int main(void){
    pthread_t ptids[5],ctids[5];
    pthread_mutex_init(&mutex,NULL);

    pthread_cond_init(&cond,NULL);

    for(int i = 0;i < 5;i++){
        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }
    for(int i = 0;i < 5;i++){
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }
    while (1)
    {
        sleep(100);
    }
    //不加 while 死循环会直接执行下面销毁互斥量的语句,那互斥量就没法用了
    

    pthread_mutex_destroy(&mutex);

    pthread_cond_destroy(&cond);
    
    pthread_exit(NULL);

   // pthread_mutex_destroy(&mutex);
   //不能放这里,放这里直接退出了就无法执行了


    return 0;
}

在这里插入图片描述
使用信号量

/*
    信号量的类型 sem_t
    int sem_init(sem_t *sem, int pshared, unsigned int value);
        - 初始化信号量
        - 参数
            - sem : 信号变量的地址
            - pshared : 0 用在线程间,非0 用在进程间
            - value : 信号量中的值

    int sem_destroy(sem_t *sem);
        - 释放资源

    int sem_wait(sem_t *sem);
        - 对信号量加锁,调用一次对信号量的值减一,如果为0就阻塞。


    int sem_trywait(sem_t *sem);

    int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
    int sem_post(sem_t *sem);
        - 对信号量解锁,调用一次对信号量减一
    int sem_getvalue(sem_t *sem, int *sval);

    sem_t psem;
    sem_t csem;
    init(psem, 0, 8);
    init(csem, 0, 0);

    producer() {
        sem_wait(&psem);
        sem_post(&csem)
    }

    customer() {
        sem_wait(&csem);
        sem_post(&psem)
    }

*/
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
//创建一个互斥量
pthread_mutex_t mutex;
sem_t psem;
sem_t csem;

struct Node
{
    int num;
    struct Node*next;
};

//头节点
struct Node*head = NULL;

void *producer(void*arg){
    //不断的创建新的节点,添加到链表中
    while (1)
    {
        sem_wait(&psem);
        pthread_mutex_lock(&mutex);
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand()%1000;
        printf("add node, num : %d,tid : %ld.\n",newNode->num,pthread_self());
    
        pthread_mutex_unlock(&mutex);
        sem_post(&csem);
        usleep(100);
    }
    
    return NULL;
}
void *customer(void*arg){
    while (1)
    {
        //保存头节点的指针
        sem_wait(&csem);
        pthread_mutex_lock(&mutex);
      
        struct Node*tmp = head;
        //判断是否有数据
       
        head = head->next;
        printf("del node,num : %d,tid : %ld.\n",tmp->num,pthread_self());
        free(tmp);
        pthread_mutex_unlock(&mutex);
        sem_post(&psem);
       
         
        }
       
        
    
    
    return NULL;
}
int main(void){
    pthread_t ptids[5],ctids[5];
    pthread_mutex_init(&mutex,NULL);

    sem_init(&psem,0,8); //表示容器空位
    sem_init(&csem,0,0);//表示生产的物品数

 

    for(int i = 0;i < 5;i++){
        pthread_create(&ptids[i],NULL,producer,NULL);
        pthread_create(&ctids[i],NULL,customer,NULL);
    }
    for(int i = 0;i < 5;i++){
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }
    while (1)
    {
        sleep(100);
    }
    //不加 while 死循环会直接执行下面销毁互斥量的语句,那互斥量就没法用了
    

    pthread_mutex_destroy(&mutex);

   
    pthread_exit(NULL);

   // pthread_mutex_destroy(&mutex);
   //不能放这里,放这里直接退出了就无法执行了


    return 0;
}

运行结果非常丝滑
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值