操作系统:lab09: Write a c program to implement product and consumer problem using multi-threads

#创作灵感#
  1. 没有找到现成代码,只有自己写了;
  2. 写都写了,花了我一天的时间,不发白不发;
  3. 可能还有bug,私聊我,但不一定改,因为我懒;
  4. 别骂我,我玻璃心,骂我我就去钟楼顶上哭;
  5. 哪位大佬写了计图的作业,share一下,真做不完了。
Target
  1. Write a c/c++ program
  2. To implement product and consumer problem using multi-threads
  3. GCC
Tools
  • Install GCC Software Collection
How to do
  • write a c program to implement product and consumer problem using multi-threads
  • 实现I个⽣产者J个消费者问题,其中共享缓冲区的⼤⼩为N,所有⽣产者共⽣产K(K>N)个产品后结束,所有消费者共消费K个产品后结束。
  • 具体要求
    • 严格按时序输出每个⽣产者、消费者的⾏为,其中包括⽣产产品k、消费产品k、进⼊临界区、存⼊产品、取出产品、离开临界区;
    • 需要考虑边界(某⽣产者⽣产第K个产品后所有⽣产者结束;某消费者消费第K个产品后所有消费者结束)
    • 需要考虑随机函数,⽣产者⽣产时需要⼀个随机时间;消费者消费时也需要⼀个随机时间;
    • 编号:⽆论⽣产者还是消费者都需要有编号;产品同样也需要编号;缓冲区的各个产品项也需要有编号;
    • 输出形式可以采⽤标准输出、图形动态显⽰及同时⽂字记录输出等⽅式,⽆论是⽣产者还是消费者,其主要输出内容如下︓ a) 进⼊临界区前,输出某某编号(⽣产者/消费者)线程准备进⼊临界区 b) 进⼊临界区后,输出某某编号(⽣产者/消费者)线程已进⼊临界区 c) 离开临界区后,输出某某编号(⽣产者/消费者)线程已离开临界区 d) ⽣产者⽣产⼀个产品时,需要输出产品信息︔ e) ⽣产者将产品放⼊缓冲区时,需要输出相关信息︔ f) 消费者将产品从缓冲区取出时,需要输出相关信息︔ g) 消费者消费⼀个产品时,需要输出产品信息;
    • 不能出现竞态
    • 不能出现忙等待

参考代码(建议改一改参数,一定程度上借鉴了GPT)

#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <jmorecfg.h>
#include <unistd.h>

#define I 5     /* 生产者数量 */
#define J 20    /* 消费者数量 */
#define N 10    /* 缓冲区数量 */
#define K 100   /* 总共需要生产的产品数量 */
#define P_MAX_TIME 5    /* 生产者生产需要花费的最大时间(单位:秒) */
#define C_MAX_TIME 10   /* 消费者消费需要花费的最大时间(单位:秒) */
#define DATA_SIZE 256

/*产品结构体声明,本案例中只用到了uid和produce_id*/
typedef struct item_st {
    int id; /*产品编号*/
    int uid; /*顺次编号*/
    time_t timestamp_p; /*⽣产时间*/
    int produce_id; /*⽣产者编号*/
    unsigned long checksum; /* 产品校验码*/
    unsigned long data[DATA_SIZE]; /* 产品详细信息*/
} ITEM_ST;

/*缓冲区结构体声明*/
typedef struct buffer_st {
    int id; /*缓冲区编号*/
    boolean isempty; /*是否为空*/
    ITEM_ST item; /*具体产品*/
    pthread_mutex_t mutex;/* 缓冲区互斥量 */
    struct buffer_st* nextbuf; /*下⼀个缓冲区⾸地址*/
} BUFFER_ST;

/* 定义全局变量 */
BUFFER_ST* g_buffer_head = NULL;    /* 缓冲区链表 */
int g_producer_count = I;           /* 生产者数量 */
int g_consumer_count = J;           /* 消费者数量 */
int produce_count = 0;              /* 已生产产品数量 */
int consume_count = 0;              /* 已消费产品数量 */
pthread_mutex_t g_count_mutex = PTHREAD_MUTEX_INITIALIZER;  /* 生产/消费数量互斥量 */
sem_t g_empty_sem;  /* 空闲缓冲区信号量 */
sem_t g_full_sem;   /* 满缓冲区信号量 */

/* 随机生成一个产品 */
void produce_item(ITEM_ST* item) {
    item->id = rand() % 1000;
    item->uid = produce_count;
}

/* 生产者线程函数:
 * 这里的逻辑是这样的(看看就好,这是改bug前写的,懒得再写了):
 * 1. 生产者进行生产,即随机休眠一段时间
 * 2. 如果生产者生产完了,但是窗口没有空闲,那么生产者就等待
 * 3. 更新生产数量(包括产品的信息)
 * 4. 生产者将产品放入窗口(缓冲区读写)
 * 5. 打印线程相关信息
 * */
void* producer_thread(void* arg) {
    int id = *(int*)arg;
    while (1) {
        /* 随机休眠一段时间 */
        int sleep_time = rand() % P_MAX_TIME;
        sleep(sleep_time);
        
        printf("---- a) 进⼊临界区前,输出 %d 编号 ⽣产者 线程准备进⼊临界区;\n", id);
        /* 等待空闲缓冲区 */
        sem_wait(&g_empty_sem);

        /* 加锁操作 */
        pthread_mutex_lock(&g_buffer_head->mutex);
        printf("------ b) 进⼊临界区后,输出 %d 编号 ⽣产者 线程已进⼊临界区;\n", id);
        
        if(produce_count >= K) {  // 需要考虑边界(某⽣产者⽣产第K个产品后所有⽣产者结束⽣产)
            /* 解锁操作 */
            pthread_mutex_unlock(&g_buffer_head->mutex);
            sem_post(&g_full_sem);
            break;
        }

        while (g_buffer_head->isempty == 0) {
            g_buffer_head = g_buffer_head->nextbuf;
        }

        /* 更新生产数量(随机生成一个产品在其中) */
        pthread_mutex_lock(&g_count_mutex);
        produce_count++;
        /* 随机生成一个产品 */
        ITEM_ST item;
        produce_item(&item);
        item.produce_id = id; /*⽣产者编号*/
        printf("-------- d) ⽣产者⽣产⼀个产品时,需要输出产品信息:uid: %d, proid: %d;\n", item.uid, item.produce_id);
        pthread_mutex_unlock(&g_count_mutex);
        
        /* 将产品放入空闲缓冲区 */
        g_buffer_head->item = item;
        g_buffer_head->isempty = 0;
        printf("---------- e) ⽣产者将产品放入缓冲区时,需要输出相关信息:%d 号 生产者 将 %d 号产品放入缓冲区;\n", id, item.uid);
        
        /* 移动链表头指针 */
        g_buffer_head = g_buffer_head->nextbuf;

        /* 解锁操作 */
        pthread_mutex_unlock(&g_buffer_head->mutex);

        /* 发送满缓冲区信号量 */
        sem_post(&g_full_sem);
        printf("------------ c) 离开临界区后,输出 %d 编号 ⽣产者 线程已离开临界区;\n", id);
    }
    return NULL;
}

/* 消费者线程函数
 * 这里的逻辑是这样的(看看就好,这是改bug前写的,懒得再写了):
 * 1. 消费者从窗口获得了一份食物(缓冲区读写)
 * 2. 更新消费数量
 * 3. 消费者进行消费,即随机休眠一段时间(这段时间应该是消费者和餐桌之间的关系,与窗口无关)
 * 4. -----
 * 5. 打印线程相关信息
 * */
void* consumer_thread(void* arg) {
    int id = *(int*)arg;
    while (consume_count < K) {
        while (consume_count >= produce_count) {
            sleep(1);
        }
        /* 等待满缓冲区 */
        sem_wait(&g_full_sem);

        /* 加锁操作 */
        pthread_mutex_lock(&g_buffer_head->mutex);
        
        while (g_buffer_head->isempty == 1) {
            g_buffer_head = g_buffer_head->nextbuf;
        }

        /* 从满缓冲区取出产品 */
        ITEM_ST item = g_buffer_head->item;
        g_buffer_head->isempty = 1;
        printf("++++++++++ f) 消费者将产品从缓冲区取出时,需要输出相关信息:%d 号 消费者 将 %d 号产品从缓冲区取出;\n", id, item.uid);
        
        /* 更新消费数量 */
        pthread_mutex_lock(&g_count_mutex);
        consume_count++;
        pthread_mutex_unlock(&g_count_mutex);
        int t_uid = item.uid;
        int t_produce_id = item.produce_id;

        /* 移动链表头指针 */
        g_buffer_head = g_buffer_head->nextbuf;

        /* 解锁操作 */
        pthread_mutex_unlock(&g_buffer_head->mutex);

        /* 发送空闲缓冲区信号量 */
        sem_post(&g_empty_sem);

        /* 随机休眠一段时间 */
        int sleep_time = rand() % C_MAX_TIME + 10;
        sleep(sleep_time);
        printf("++++++++ g) 消费者消费⼀个产品时,需要输出产品信息:uid: %d, proid: %d;\n", t_uid, t_produce_id);

        /* 消费产品 */
        // TODO: 进行产品消费的逻辑处理,即消费者和餐桌之间的关系,建议写实验报告的时候要么把这段逻辑删掉,要么在报告里提一句
    }
    return NULL;
}

int main() {
    /* 初始化随机数生成器 */
    srand(time(NULL));

    /* 创建缓冲区链表 */
    BUFFER_ST* prev_buf = NULL;
    for (int i = 0; i < N; i++) {
        BUFFER_ST* buf = (BUFFER_ST*)malloc(sizeof(BUFFER_ST));
        buf->id = i;
        buf->isempty = 1;
        if (prev_buf != NULL) {
            prev_buf->nextbuf = buf;
        } else {
            g_buffer_head = buf;
        }
        pthread_mutex_init(&buf->mutex, NULL);
        prev_buf = buf;
    }
    prev_buf->nextbuf = g_buffer_head;

    /* 初始化信号量 */
    sem_init(&g_empty_sem, 0, N);
    sem_init(&g_full_sem, 0, 0);

    /* 创建生产者线程和消费者线程 */
    pthread_t producer_threads[g_producer_count];
    pthread_t consumer_threads[g_consumer_count];

    int producer_ids[g_producer_count];
    int consumer_ids[g_consumer_count];

    for (int i = 0; i < g_producer_count; i++) {
        producer_ids[i] = i;
        pthread_create(&producer_threads[i], NULL, producer_thread, (void*)&producer_ids[i]);
    }

    sleep(10);  /* 等待生产者线程先运行 */

    for (int i = 0; i < g_consumer_count; i++) {
        consumer_ids[i] = i;
        pthread_create(&consumer_threads[i], NULL, consumer_thread, (void*)&consumer_ids[i]);
    }

    /* 等待生产者线程和消费者线程结束 */
    for (int i = 0; i < g_producer_count; i++) {
        pthread_join(producer_threads[i], NULL);
    }

    for (int i = 0; i < g_consumer_count; i++) {
        pthread_join(consumer_threads[i], NULL);
    }

    /* 销毁互斥量和信号量 */
    pthread_mutex_destroy(&g_count_mutex);
    sem_destroy(&g_empty_sem);
    sem_destroy(&g_full_sem);

    /* 释放缓冲区链表内存 */
    BUFFER_ST* curr_buf = g_buffer_head;
    while (curr_buf->nextbuf != g_buffer_head) {
        BUFFER_ST* next_buf = curr_buf->nextbuf;
        pthread_mutex_destroy(&curr_buf->mutex);
        free(curr_buf);
        curr_buf = next_buf;
    }
    pthread_mutex_destroy(&curr_buf->mutex);
    free(curr_buf);

    return 0;
}
  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值