欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > 【操作系统】读者-写者问题

【操作系统】读者-写者问题

2025/5/5 15:13:26 来源:https://blog.csdn.net/m0_37806005/article/details/147702579  浏览:    关键词:【操作系统】读者-写者问题

问题描述

        读者-写者问题是并发编程中的一个经典问题,描述了一个场景,其中多个读者和多个写者共享一个资源(如文件或数据库)。问题的核心是如何协调读者和写者的访问,以确保:

  1. 多个读者可以同时读取资源,但不允许写者访问。

  2. 写者独占资源,即写者访问时,不允许其他读者或写者访问。

问题的三种变体

  1. 第一种变体:优先考虑读者,允许读者尽可能多地访问资源,但写者可能会饿死(即长时间等待)。

  2. 第二种变体:优先考虑写者,写者一旦准备好就可以立即访问资源,但读者可能会饿死。

  3. 第三种变体:读者和写者都应公平地访问资源,避免饿死。

解决方案

        以下是一个基于POSIX信号量的解决方案,优先考虑读者(第一种变体)。

 伪代码

 读者逻辑(Reader)

初始化信号量:mutex = 1  // 用于互斥访问读者计数器wrt = 1    // 用于写者访问rc = 0     // 读者计数器读者线程:while (true) {// 想要读P(mutex)  // 进入临界区rc++     // 增加读者计数if (rc == 1) {P(wrt)  // 阻止写者访问}V(mutex)  // 离开临界区// 正在读打印 "Reader <reader_id> is reading"等待一段时间 (模拟读取时间)// 读完P(mutex)  // 进入临界区rc--     // 减少读者计数if (rc == 0) {V(wrt)  // 允许写者访问}V(mutex)  // 离开临界区等待一段时间 (模拟读者等待时间)}

写者逻辑(Writer)

初始化信号量:wrt = 1  // 用于写者访问写者线程:while (true) {// 想要写P(wrt)  // 阻止读者和其他写者访问打印 "Writer <writer_id> is writing"等待一段时间 (模拟写入时间)V(wrt)  // 允许读者和其他写者访问等待一段时间 (模拟写者等待时间)}

伪代码说明

  1. 信号量操作

    • P(mutex)V(mutex):分别表示对互斥信号量mutex的P操作(等待)和V操作(释放)。

    • P(wrt)V(wrt):分别表示对写者信号量wrt的P操作(等待)和V操作(释放)。

  2. 读者逻辑

    • 读者进入时,增加读者计数器rc

    • 如果是第一个读者,阻塞写者访问。

    • 读者离开时,减少读者计数器rc

    • 如果是最后一个读者,允许写者访问。

  3. 写者逻辑

    • 写者进入时,阻塞读者和其他写者访问。

    • 写者离开时,允许读者和其他写者访问。

伪代码中的符号

  • P(semaphore):表示对信号量semaphore进行P操作(等待)。

  • V(semaphore):表示对信号量semaphore进行V操作(释放)。

  • rc:读者计数器。

  • mutex:互斥信号量,用于保护读者计数器rc

  • wrt:写者信号量,用于控制写者对资源的访问。

 示例代码

 1. 读者代码(reader.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>// 信号量定义
sem_t mutex;       // 用于互斥访问读者计数器
sem_t wrt;         // 用于写者访问
int rc = 0;        // 读者计数器void* reader(void* arg) {while (1) {// 想要读sem_wait(&mutex); // 进入临界区rc++;            // 增加读者计数if (rc == 1) {   // 如果是第一个读者sem_wait(&wrt); // 阻止写者访问}sem_post(&mutex); // 离开临界区// 正在读printf("Reader %ld is reading\n", (long)arg);sleep(1); // 模拟读取时间// 读完sem_wait(&mutex); // 进入临界区rc--;            // 减少读者计数if (rc == 0) {   // 如果是最后一个读者sem_post(&wrt); // 允许写者访问}sem_post(&mutex); // 离开临界区sleep(1); // 模拟读者等待时间}
}int main() {pthread_t reader_thread1, reader_thread2;// 初始化信号量sem_init(&mutex, 0, 1);sem_init(&wrt, 0, 1);// 创建读者线程pthread_create(&reader_thread1, NULL, reader, (void*)1);pthread_create(&reader_thread2, NULL, reader, (void*)2);// 等待线程结束pthread_join(reader_thread1, NULL);pthread_join(reader_thread2, NULL);// 清理信号量sem_destroy(&mutex);sem_destroy(&wrt);return 0;
}

 2. 写者代码(writer.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>// 信号量定义
sem_t wrt;         // 用于写者访问void* writer(void* arg) {while (1) {// 想要写sem_wait(&wrt); // 阻止读者和其他写者访问printf("Writer %ld is writing\n", (long)arg);sleep(1); // 模拟写入时间sem_post(&wrt); // 允许读者和其他写者访问sleep(1); // 模拟写者等待时间}
}int main() {pthread_t writer_thread1, writer_thread2;// 初始化信号量sem_init(&wrt, 0, 1);// 创建写者线程pthread_create(&writer_thread1, NULL, writer, (void*)1);pthread_create(&writer_thread2, NULL, writer, (void*)2);// 等待线程结束pthread_join(writer_thread1, NULL);pthread_join(writer_thread2, NULL);// 清理信号量sem_destroy(&wrt);return 0;
}

小结

        第一种变体的实现优先考虑读者,允许多个读者同时访问资源,但写者一旦开始写入,会阻塞所有读者和其他写者。这种策略适合读操作频繁的场景,但可能会导致写者饿死。如果需要更公平的策略,可以调整信号量的逻辑,例如引入额外的信号量来控制写者的优先级。所以可以采用第三种变体,读者和写者都应公平地访问资源,避免饿死。

第三种变体

        第三种变体:读者和写者都应公平地访问资源,避免饿死。

伪代码

读者逻辑

​信号量 mutex = 1;       // 用于互斥访问读者计数器
信号量 wrt = 1;         // 用于写者访问
信号量 can_read = 1;    // 用于控制读者的访问
信号量 can_write = 1;   // 用于控制写者的访问
整数 rc = 0;            // 读者计数器
整数 wc = 0;            // 写者计数器
while (true) {// 等待可以读P(can_read);// 想要读P(mutex);  // 进入临界区rc++;      // 增加读者计数if (rc == 1) {  // 如果是第一个读者P(wrt);     // 阻止写者访问}V(mutex);  // 离开临界区// 正在读打印 "Reader <reader_id> is reading";等待一段时间 (模拟读取时间);// 读完P(mutex);  // 进入临界区rc--;      // 减少读者计数if (rc == 0) {  // 如果是最后一个读者V(wrt);     // 允许写者访问}V(mutex);  // 离开临界区// 释放读的权限V(can_write);  // 允许写者访问等待一段时间 (模拟读者等待时间);
}

写着逻辑 

信号量 wrt = 1;         // 用于写者访问
信号量 can_read = 1;    // 用于控制读者的访问
信号量 can_write = 1;   // 用于控制写者的访问
while (true) {// 等待可以写P(can_write);// 想要写P(wrt);  // 阻止读者和其他写者访问打印 "Writer <writer_id> is writing";等待一段时间 (模拟写入时间);V(wrt);  // 允许读者和其他写者访问// 释放写的权限V(can_read);  // 允许读者访问等待一段时间 (模拟写者等待时间);
}

伪代码说明

  1. 信号量操作

    • P(semaphore):表示对信号量semaphore进行P操作(等待)。

    • V(semaphore):表示对信号量semaphore进行V操作(释放)。

  2. 读者逻辑

    • 读者在访问资源之前,必须等待can_read信号量。

    • 当第一个读者开始读取时,通过P(wrt)阻塞写者。

    • 当最后一个读者结束读取时,通过V(wrt)允许写者访问。

    • 读者完成读取后,释放can_write信号量,允许写者访问。

  3. 写者逻辑

    • 写者在访问资源之前,必须等待can_write信号量。

    • 写者通过P(wrt)阻塞读者和其他写者。

    • 写者完成写入后,通过V(wrt)允许读者和其他写者访问。

    • 写者完成写入后,释放can_read信号量,允许读者访问。

伪代码中的符号

  • P(semaphore):表示对信号量semaphore进行P操作(等待)。

  • V(semaphore):表示对信号量semaphore进行V操作(释放)。

  • rc:读者计数器。

  • wc:写者计数器。

  • mutex:互斥信号量,用于保护读者计数器rc

  • wrt:写者信号量,用于控制写者对资源的访问。

  • can_read:控制读者的访问。

  • can_write:控制写者的访问。

示例代码

读者代码(reader.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>// 信号量定义
sem_t mutex;       // 用于互斥访问读者计数器
sem_t wrt;         // 用于写者访问
sem_t can_read;    // 用于控制读者的访问
int rc = 0;        // 读者计数器void* reader(void* arg) {while (1) {// 等待可以读sem_wait(&can_read);// 想要读sem_wait(&mutex); // 进入临界区rc++;            // 增加读者计数if (rc == 1) {   // 如果是第一个读者sem_wait(&wrt); // 阻止写者访问}sem_post(&mutex); // 离开临界区// 正在读printf("Reader %ld is reading\n", (long)arg);sleep(1); // 模拟读取时间// 读完sem_wait(&mutex); // 进入临界区rc--;            // 减少读者计数if (rc == 0) {   // 如果是最后一个读者sem_post(&wrt); // 允许写者访问}sem_post(&mutex); // 离开临界区// 释放读的权限sem_post(&can_read);sleep(1); // 模拟读者等待时间}
}int main() {pthread_t reader_thread1, reader_thread2;// 初始化信号量sem_init(&mutex, 0, 1);sem_init(&wrt, 0, 1);sem_init(&can_read, 0, 1);// 创建读者线程pthread_create(&reader_thread1, NULL, reader, (void*)1);pthread_create(&reader_thread2, NULL, reader, (void*)2);// 等待线程结束pthread_join(reader_thread1, NULL);pthread_join(reader_thread2, NULL);// 清理信号量sem_destroy(&mutex);sem_destroy(&wrt);sem_destroy(&can_read);return 0;
}

写者代码(writer.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>// 信号量定义
sem_t wrt;         // 用于写者访问
sem_t can_read;    // 用于控制读者的访问void* writer(void* arg) {while (1) {// 想要写sem_wait(&wrt); // 阻止读者和其他写者访问printf("Writer %ld is writing\n", (long)arg);sleep(1); // 模拟写入时间sem_post(&wrt); // 允许读者和其他写者访问// 释放读的权限sem_post(&can_read);sleep(1); // 模拟写者等待时间}
}int main() {pthread_t writer_thread1, writer_thread2;// 初始化信号量sem_init(&wrt, 0, 1);sem_init(&can_read, 0, 1);// 创建写者线程pthread_create(&writer_thread1, NULL, writer, (void*)1);pthread_create(&writer_thread2, NULL, writer, (void*)2);// 等待线程结束pthread_join(writer_thread1, NULL);pthread_join(writer_thread2, NULL);// 清理信号量sem_destroy(&wrt);sem_destroy(&can_read);return 0;
}

代码说明

  1. 信号量的使用

    • mutex信号量用于保护读者计数器rc,确保读者计数器的增减操作是原子的。

    • wrt信号量用于控制写者对共享资源的访问,确保写者独占资源。

  2. 读者逻辑

    • 当第一个读者开始读取时,通过sem_wait(&wrt)阻塞写者。

    • 当最后一个读者结束读取时,通过sem_post(&wrt)允许写者访问。

    • 多个读者可以同时访问共享资源,因为rc计数器确保了写者不会在读者读取期间访问资源。

  3. 写者逻辑

    • 写者通过sem_wait(&wrt)阻塞读者和其他写者。

    • 写者完成写操作后,通过sem_post(&wrt)允许读者和其他写者访问。

小结

        通过引入can_read信号量,我们确保了读者和写者交替访问资源,避免了读者或写者饿死的问题。这种实现符合第三种变体的要求,即读者和写者都应公平地访问资源。

 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词