C語言:生產者-消費者問題、讀者-寫者問題

1.生產者-消費者問題:

題目要求
• 在 6.6.1 小節中,使用了三個信號量: empty (以記錄有多少空位)、full
(以記錄有多少滿位)以及 mutex (二進制信號量或互斥信號量,以保
護對緩衝插入與刪除的操作)。對於本項目, empty 與 full 將採用標
準計數信號量,而 mutex 將採用二進制信號量。生產者與消費者作
爲獨立線程,在 empty、full、mutex 的同步前提下,對緩衝進行插
入與刪除。
• 本項目,可採用 Pthread 。

測試文件格式
測試數據文件包括 n 行測試數據,分別描述創建的 n 個線程是生產者還
是消費者,以及生產者或消費者存放或取產品的開始時間和持續時間。
每行測試數據包括四個字段,各個字段間用空格分隔。第一字段爲一個
正整數,表示線程序號。第二字段表示相應線程角色,P 表示生產者,
C 表示消費者。第三字段爲一個正數,表示存放或取出操作的開始時間:
線程創建後,延遲相應時間(單位爲秒)後發出對共享資源的使用申請。
第四字段爲一個正數,表示操作的持續時間。第五字段爲一個正數(僅
生產者有),表示生產的產品號。當線程申請成功後,開始對共享資源的
操作,該操作持續相應時間後結束,並釋放共享資源。

# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>

# define BUFFER_SIZE 10

// 定義線程執行函數傳入的參數格式
struct p {
    int id; // 線程的編號
    int num; // 產品
    int runtime; // 運行的時間
    int sleeptime; // 延遲發出請求的時間
};

int buffer[BUFFER_SIZE+1]; // 緩衝區的定義
int front = 0, rear = 0; // 標識頭尾指針
sem_t mutex, empty, full; // 本項目需要用到的信號量

// 生產者生產元素
// @param item 生產的值
int insertItem(int item) {
    if ((rear+1)%(BUFFER_SIZE+1) == front) return 1;
    buffer[rear] = item;
    rear = (rear+1)%(BUFFER_SIZE+1);
    return 0;
}

// 消費者消費元素
// @param 用於存放消費者的產品
int removeItem(int* item) {
    if (front == rear) return 1;
    *item = buffer[front];
    front = (front+1) % (BUFFER_SIZE+1);
    return 0;
}

// 生產者的定義
void* producer(void* Param) {
    struct p data = *(struct p*)Param;
    sleep(data.sleeptime);
    sem_wait(&empty);
    sem_wait(&mutex);
    printf("producer has produce %d By Thread %d\n", data.num, data.id);
    if (insertItem(data.num))
        printf("Insert error");
    sleep(data.runtime);
    sem_post(&mutex);
    sem_post(&full);
    pthread_exit(0);
}

// 消費者的定義
void* consumer(void* Param) {
    struct p data =*(struct p*)Param;
    sleep(data.runtime);
    sem_wait(&full);
    sem_wait(&mutex);
    if (removeItem(&data.num))
        printf("remove error");
    else
        printf("consumer consumed %d By Thread %d\n", data.num, data.id);
    sleep(data.runtime);
    sem_post(&mutex);
    sem_post(&empty);
    pthread_exit(0);
}

int main(int argc, char* argv[]) {
    int Thread_num, i;
    if (argc != 2) {
        fprintf(stderr, "usage:./a.out <integer value>\n");
        return -1;
    } else {
        Thread_num = atoi(argv[1]); // 獲取輸入的行數
    }
    struct p* data;
    pthread_t* tid;
    pthread_attr_t* attr;

    // 初始化信號量
    sem_init(&mutex, 0, 1);
    sem_init(&empty, 0, BUFFER_SIZE);
    sem_init(&full, 0, 0);

    // 動態生成數組
    data = (struct p*)malloc((Thread_num+1)*sizeof(struct p));
    tid = (pthread_t*)malloc((Thread_num+1)*sizeof(pthread_t));
    attr = (pthread_attr_t*)malloc((Thread_num+1)*sizeof(pthread_attr_t));

    // 根據每一行輸入創建線程
    for (i = 0; i < Thread_num; i++) {
        pthread_attr_init(&attr[i]);
        scanf("%d", &data[i].id);
        getchar();
        char C;
        scanf("%c", &C);
        if (C == 'P') {
            scanf("%d", &(data[i].sleeptime));
            scanf("%d", &(data[i].runtime));
            scanf("%d", &(data[i].num));
            pthread_create(&tid[i], &attr[i], producer, &data[i]);
            //pthread_join(tid, NULL);
            //ThreadHandle[i] = CreateThread(NULL, 0, producer, &data[i], 0, &ThreadId[i]);
        } else if (C == 'C') {
            scanf("%d", &(data[i].sleeptime));
            scanf("%d", &(data[i].runtime));
            pthread_create(&tid[i], &attr[i], consumer, &data[i]);
            //pthread_join(tid, NULL);
            //ThreadHandle[i] = CreateThread(NULL, 0, consumer, &data[i], 0, &ThreadId[i]); 
        }
        //pthread_join(tid[i], NULL);
    }
    //sleep(20);
    sleep(1000);

    // 信號量的銷燬
    sem_destroy(&mutex);
    sem_destroy(&empty);
    sem_destroy(&full);

    return 0;
}

測試數據:
1 C 3 5
2 P 4 5 1
3 C 5 2
4 C 6 5
5 P 7 3 2

實驗結果:
這裏寫圖片描述

2. 讀者-寫者問題

題目要求
• 在 Linux 環境下,創建一個進程,此進程包含 n 個線程。用這 n 個線
程來表示 n 個讀者或寫者。每個線程按相應測試數據文件(後面有介
紹)的要求進行讀寫操作。用信號量機制分別實現讀者優先和寫者優
先的讀者-寫者問題。
• 讀者-寫者問題的讀寫操作限制(僅讀者優先或寫者優先):
1)寫-寫互斥,即不能有兩個寫者同時進行寫操作。
2)讀-寫互斥,即不能同時有一個線程在讀,而另一個線程在寫。
3)讀-讀允許,即可以有一個或多個讀者在讀。
• 讀者優先的附加限制:如果一個讀者申請進行讀操作時已有另一個讀
者正在進行讀操作,則該讀者可直接開始讀操作。
• 寫者優先的附加限制:如果一個讀者申請進行讀操作時已有另一寫者
在等待訪問共享資源,則該讀者必須等到沒有寫者處於等待狀態後才
能開始讀操作。
• 運行結果顯示要求:要求在每個線程創建、發出讀寫操作申請、開始
讀寫操作和結束讀寫操作時分別顯示一行提示信息,以確定所有處理
都遵守相應的讀寫操作限制。

測試文件格式
測試數據文件包括 n 行測試數據,分別描述創建的 n 個線程是讀者還是
寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括四個字
段,各個字段間用空格分隔。第一字段爲一個正整數,表示線程序號。
第二字段表示相應線程角色,R 表示讀者,W 表示寫者。第三字段爲一
個正數,表示讀寫操作的開始時間:線程創建後,延遲相應時間(單位爲
秒)後發出對共享資源的讀寫申請。第四字段爲一個正數,表示讀寫操作
的持續時間。當線程讀寫申請成功後,開始對共享資源的讀寫操作,該
操作持續相應時間後結束,並釋放共享資源

# include <stdio.h>
# include <stdlib.h>
# include <time.h>
# include <sys/types.h>
# include <pthread.h>
# include <semaphore.h>
# include <string.h>

//int _readcount;
//int _wrt;

// 定義傳入線程執行函數的參數
struct p {
    int id;
    int runtime;
    int sleeptime;
};

// 信號量的定義
sem_t readcount, wrt, mutex;

// 寫者定義
void* writer(void* param) {
    struct p data = *(struct p*)param;
    sleep(data.sleeptime);
    sem_wait(&wrt);
    printf("Process %d, writing!\n", data.id);
    sleep(data.runtime);
    sem_post(&wrt);
    pthread_exit(0);
}

// 讀者定義
void* reader(void* param) {
    struct p data = *(struct p*)param;
    sleep(data.sleeptime);
    sem_wait(&mutex);
    //readcount++;
    int value;
    sem_getvalue(&readcount, &value);
    sem_init(&readcount, 0, value+1);
    sem_getvalue(&readcount, &value);
    if (value == 1)
        sem_wait(&wrt);
    sem_post(&mutex);
    printf("Process %d, reading!\n", data.id);
    sleep(data.runtime);
    sem_wait(&mutex);
    //readcount--;
    sem_getvalue(&readcount, &value);
    sem_init(&readcount, 0, value-1);
    sem_getvalue(&readcount, &value);
    if (value == 0)
        sem_post(&wrt);
    sem_post(&mutex);
    pthread_exit(0);
}

// 主函數的設計
int main(int argc, char* argv[]) {
    int Thread_num, i;
    if (argc != 2) {
        fprintf(stderr, "usage:./a.out <integer value>\n");
        return -1;
        //Thread_num = 2;
    } else {
        Thread_num = atoi(argv[1]);
    }
    struct p* data;
    pthread_t* tid;
    pthread_attr_t* attr;
    sem_init(&mutex, 0, 1);
    sem_init(&readcount, 0, 0);
    sem_init(&wrt, 0, 1);
    data = (struct p*)malloc((Thread_num+1)*sizeof(struct p));
    tid = (pthread_t*)malloc((Thread_num+1)*sizeof(pthread_t));
    attr = (pthread_attr_t*)malloc((Thread_num+1)*sizeof(pthread_attr_t));
    for (i = 0; i < Thread_num; i++) {
        pthread_attr_init(&attr[i]);
        scanf("%d", &data[i].id);
        getchar();
        char C;
        scanf("%c", &C);
        if (C == 'R') {
            scanf("%d", &(data[i].sleeptime));
            scanf("%d", &(data[i].runtime));
            pthread_create(&tid[i], &attr[i], reader, &data[i]);
        } else if (C == 'W') {
            scanf("%d", &(data[i].sleeptime));
            scanf("%d", &(data[i].runtime));
            pthread_create(&tid[i], &attr[i], writer, &data[i]);
        }
    }
    sleep(1000);
    sem_destroy(&mutex);
    sem_destroy(&readcount);
    sem_destroy(&wrt);
    return 0;
}

測試數據:
1 R 3 5
2 W 4 5
3 R 5 2
4 R 6 5
5 W 7 3

實驗結果:
這裏寫圖片描述


以上內容皆爲本人觀點,歡迎大家提出批評和指導,我們一起探討!


發佈了146 篇原創文章 · 獲贊 268 · 訪問量 53萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章