C语言实现 操作系统 经典的进程同步问题(3)

读者-写者问题

1、利用记录型信号量解决读者-写者问题

通过POSIX信号量实现。

cpp 复制代码
#include <stdio.h>  
#include <stdlib.h>  
#include <pthread.h>  
#include <semaphore.h>  
#include <unistd.h>  
  
#define NUM_READERS 5  
#define NUM_WRITERS 3  
#define READ_TIME 2  
#define WRITE_TIME 1  
  
sem_t rw_mutex;      // 保护共享资源的互斥锁  
sem_t mutex;         // 保护读者计数的互斥锁  
int read_count = 0;  // 当前正在读取的读者数量  
  
void *reader(void *arg) {  
    int reader_id = *((int *)arg);  
    while (1) {  
        // 请求读取锁  
        sem_wait(&mutex);  
        read_count++;  
        if (read_count == 1) {  
            sem_wait(&rw_mutex);  // 第一个读者获得写锁(独占)  
        }  
        sem_post(&mutex);  
  
        // 读取操作  
        printf("Reader %d is reading.\n", reader_id);  
        sleep(READ_TIME);  
  
        // 释放读取锁  
        sem_wait(&mutex);  
        read_count--;  
        if (read_count == 0) {  
            sem_post(&rw_mutex);  // 最后一个读者释放写锁  
        }  
        sem_post(&mutex);  
  
        sleep(1);  // 读者休息一会儿再尝试读取  
    }  
    return NULL;  
}  
  
void *writer(void *arg) {  
    int writer_id = *((int *)arg);  
    while (1) {  
        // 请求写入锁  
        sem_wait(&rw_mutex);  
  
        // 写入操作  
        printf("Writer %d is writing.\n", writer_id);  
        sleep(WRITE_TIME);  
  
        // 释放写入锁  
        sem_post(&rw_mutex);  
  
        sleep(1);  // 写者休息一会儿再尝试写入  
    }  
    return NULL;  
}  
  
int main() {  
    pthread_t readers[NUM_READERS], writers[NUM_WRITERS];  
    int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];  
  
    // 初始化信号量  
    sem_init(&rw_mutex, 0, 1);  
    sem_init(&mutex, 0, 1);  
  
    // 创建读者线程  
    for (int i = 0; i < NUM_READERS; i++) {  
        reader_ids[i] = i;  
        pthread_create(&readers[i], NULL, reader, &reader_ids[i]);  
    }  
  
    // 创建写者线程  
    for (int i = 0; i < NUM_WRITERS; i++) {  
        writer_ids[i] = i;  
        pthread_create(&writers[i], NULL, writer, &writer_ids[i]);  
    }  
  
    // 等待线程结束(实际上这些线程是无限循环的,这里只是为了示例)  
    for (int i = 0; i < NUM_READERS; i++) {  
        pthread_join(readers[i], NULL);  
    }  
    for (int i = 0; i < NUM_WRITERS; i++) {  
        pthread_join(writers[i], NULL);  
    }  
  
    // 销毁信号量  
    sem_destroy(&rw_mutex);  
    sem_destroy(&mutex);  
  
    return 0;  
}

信号量初始化:

  • rw_mutex:用于保护共享资源的互斥锁,初始值为1,表示资源可用。
  • mutex:用于保护读者计数read_count的互斥锁,初始值为1,表示可访问。

读者线程:

  • 使用sem_wait(&mutex)请求访问读者计数互斥锁。
  • 增加read_count,如果这是第一个读者,则请求rw_mutex锁,独占资源。
  • 释放mutex锁。
  • 执行读取操作。
  • 再次请求mutex锁,减少read_count,如果这是最后一个读者,则释放rw_mutex锁。
  • 释放mutex锁。

写者线程:

  • 直接请求rw_mutex锁,独占资源。
  • 执行写入操作。
  • 释放rw_mutex锁。

主函数:

  • 创建读者和写者线程。
  • 等待线程结束(这里示例代码中的线程是无限循环的,实际使用时可能需要加入退出条件)。
  • 销毁信号量

2、利用信号量集机制解决读者-写者问题

使用了信号量(semaphores)来管理对共享资源的访问。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

#define NUM_READERS 5
#define NUM_WRITERS 3
#define READ_TIME 2
#define WRITE_TIME 1

sem_t rw_mutex;      // 保护共享资源的互斥锁
sem_t mutex;         // 保护读者计数的互斥锁
int read_count = 0;  // 当前正在读取的读者数量

void *reader(void *arg) {
    int reader_id = *((int *)arg);
    while (1) {
        sem_wait(&mutex);
        read_count++;
        if (read_count == 1) {
            sem_wait(&rw_mutex);
        }
        sem_post(&mutex);

        printf("Reader %d is reading.\n", reader_id);
        sleep(READ_TIME);

        sem_wait(&mutex);
        read_count--;
        if (read_count == 0) {
            sem_post(&rw_mutex);
        }
        sem_post(&mutex);

        sleep(1);
    }
    return NULL;
}

void *writer(void *arg) {
    int writer_id = *((int *)arg);
    while (1) {
        sem_wait(&rw_mutex);

        printf("Writer %d is writing.\n", writer_id);
        sleep(WRITE_TIME);

        sem_post(&rw_mutex);

        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t readers[NUM_READERS], writers[NUM_WRITERS];
    int reader_ids[NUM_READERS], writer_ids[NUM_WRITERS];

    sem_init(&rw_mutex, 0, 1);
    sem_init(&mutex, 0, 1);

    for (int i = 0; i < NUM_READERS; i++) {
        reader_ids[i] = i;
        pthread_create(&readers[i], NULL, reader, &reader_ids[i]);
    }

    for (int i = 0; i < NUM_WRITERS; i++) {
        writer_ids[i] = i;
        pthread_create(&writers[i], NULL, writer, &writer_ids[i]);
    }

    for (int i = 0; i < NUM_READERS; i++) {
        pthread_join(readers[i], NULL);
    }
    for (int i = 0; i < NUM_WRITERS; i++) {
        pthread_join(writers[i], NULL);
    }

    sem_destroy(&rw_mutex);
    sem_destroy(&mutex);

    return 0;
}

变量定义

  • NUM_READERSNUM_WRITERS:分别定义了读者的数量和写者的数量。
  • READ_TIMEWRITE_TIME:分别定义了读者读取和写者写入共享资源所需的时间(以秒为单位)。
  • rw_mutex:一个信号量,用于保护对共享资源的访问,确保写者在写入时具有独占权。
  • mutex:一个信号量,用于保护对read_count(当前正在读取的读者数量)的访问,确保读者计数操作的原子性。
  • read_count:当前正在读取的读者数量。

读者线程(reader函数)

  • 每个读者线程首先获取mutex信号量,以确保对read_count的访问是原子的。
  • 然后,读者增加read_count的值。
  • 如果这是第一个读者(read_count == 1),它会获取rw_mutex信号量,以阻止写者和其他读者开始读取,直到当前读者组完成读取。
  • 释放mutex信号量,允许其他读者修改read_count
  • 读者开始读取(通过sleep(READ_TIME)模拟读取时间)。
  • 再次获取mutex信号量,准备修改read_count
  • 减少read_count的值。
  • 如果这是最后一个读者(read_count == 0),它会释放rw_mutex信号量,允许写者开始写入。
  • 释放mutex信号量。
  • 休眠一段时间(sleep(1)),然后重复上述过程。

写者线程(writer函数)

  • 每个写者线程首先获取rw_mutex信号量,以确保对共享资源的独占访问。
  • 写者开始写入(通过sleep(WRITE_TIME)模拟写入时间)。
  • 释放rw_mutex信号量,允许其他读者或写者访问共享资源。
  • 休眠一段时间(sleep(1)),然后重复上述过程。

主函数(main

  • 初始化rw_mutexmutex信号量。
  • 创建读者线程和写者线程,每个线程都运行其相应的函数(readerwriter)。
  • 使用pthread_join等待所有读者和写者线程完成(实际上,由于线程在一个无限循环中运行,这里的pthread_join会永远阻塞)。
  • 销毁rw_mutexmutex信号量。

注意

  • 这段代码中的读者和写者线程在一个无限循环中运行,这意味着程序永远不会自然结束。在实际应用中,你可能需要实现一种机制来优雅地停止这些线程。
  • 使用sleep函数来模拟读取和写入时间是出于简化的目的。在实际应用中,你可能会有更复杂的逻辑来处理读取和写入操作。
  • pthread_join的调用意味着主线程将等待所有读者和写者线程完成,但由于这些线程在无限循环中运行,程序将不会自然结束。
相关推荐
‘’林花谢了春红‘’1 小时前
C++ list (链表)容器
c++·链表·list
搬砖的小码农_Sky2 小时前
C语言:数组
c语言·数据结构
机器视觉知识推荐、就业指导3 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Swift社区3 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman4 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
IT 青年4 小时前
数据结构 (1)基本概念和术语
数据结构·算法
Yang.994 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王5 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_5 小时前
C++自己写类 和 运算符重载函数
c++