--- title: "[学习笔记]经典的读者、写者问题" pubDate: 2025-06-13 description: '关于读写者算法的学习笔记' author: "三叶" image: url: "https://files.seeusercontent.com/2026/03/25/cd8O/pasted-image-1774458552123.webp" alt: "img" tags: ["算法", "操作系统", "学习笔记"] --- ## 首先我们来理解下读者、写者的概念 计算机中有**读者**、**写者**两个并发进程,共享一个文件。 允许两个及以上的读进程同时访问共享数据,但不允许某个写进程和其他进程同时操作。 **因此要求:** 1️⃣ 允许多个读者可以同时对文件执行读操作 2️⃣ 只允许一个写者往文件中写信息 3️⃣ 任一写者在完成写操作之前不允许其他读者或写者工作 4️⃣ 写者执行写操作前,应让已有的读者和写者全部退出 即**读进程和读进程可并发,而写进程和读/写进程均互斥**。 ## 该如何实现呢? ## 其中一种思路 我们很容易想到,可以给缓冲区上一个读写锁,当写进程开始往缓冲区中写数据时上锁,不允许其他进程访问缓冲区。 那如何实现读进程并行呢? 我们设置一个信号量`count`,每一个读进程开始读数据时count++,当它结束读数据后count--。 那事情就简单了,第一个读进程开始读数据时上锁,最后一个读数据结束时解锁。 那我们上代码。 semaphore rw = 1; // 读写锁,用于实现共享进程的互斥访问 int count = 0; // 记录有几个读进程在访问文件 semaphore mutex = 1; // count的读写锁,防止同时有两个读进程访问count导致其中一个死锁
writer () { while(1){ P(rw); // 给缓冲区上锁,进行P操作, rw-- 写文件; V(rw); // 解锁,进行V操作,rw++ } } reader () { while(1){ P(mutex); // 给count上锁,防止有两个读进程同时读到count==0,导致死锁。 if (count == 0){ P(rw); // 如果count为0,则检查缓冲区是否上锁,若上锁则等待,若无锁则为缓冲区上锁。 } count++; V(mutex); // 解除count锁 读文件; P(mutex); // 给count上锁,防止有两个进程同时读到count==0,导致rw+2产生异常 count--; if (count == 0){ V(rw); // 如果count为0,则代表这个进程为最后一个读进程,解除缓冲区的锁。 } V(mutex); } } ``` semaphore rw = 1; // 读写锁,用于实现共享进程的互斥访问 int count = 0; // 记录有几个读进程在访问文件 semaphore mutex = 1; // count的读写锁,防止同时有两个读进程访问count导致其中一个死锁 writer () { while(1){ P(rw); // 给缓冲区上锁,进行P操作, rw-- 写文件; V(rw); // 解锁,进行V操作,rw++ } } reader () { while(1){ P(mutex); // 给count上锁,防止有两个读进程同时读到count==0,导致死锁。 if (count == 0){ P(rw); // 如果count为0,则检查缓冲区是否上锁,若上锁则等待,若无锁则为缓冲区上锁。 } count++; V(mutex); // 解除count锁 读文件; P(mutex); // 给count上锁,防止有两个进程同时读到count==0,导致rw+2产生异常 count--; if (count == 0){ V(rw); // 如果count为0,则代表这个进程为最后一个读进程,解除缓冲区的锁。 } V(mutex); } } ``` **但是我们会发现这个思路有个问题。** **如果一直有读进程进来怎么办?** 如果一直有读进程申请读缓冲区(这在操作系统中很常见),那么count一直不为0,会导致写进程始终处于忙等,最终导致饥饿。**这种算法会导致读进程具有最高优先级。** ### 另一种读写公平的思路 既然读进程队列不空就无法写,那我们不如这么办: 当有写进程请求缓冲区资源时,**先将在此写进程之后来的所有读进程阻塞**,不进入读进程队列。 待读进程队列消耗完,写进程往缓冲区中写入数据结束之后,再将后来的读进程放进来。 那么我们上代码: semaphore rw = 1; int count = 0; semaphore mutex = 1; semephore w = 1; // 用于实现写优先 writer () { while (1) { P(w); // 写进程先给w锁上锁,上锁后后来的读进程都需等待这个写进程解锁。 P(rw); // 给缓冲区上锁 写文件; V(rw); V(w); // 解锁,允许读进程加入读队列。 } } reader () { while (1) { P(w); // 新的读进程加入时先检查w锁是否上锁,若已上锁则阻塞等待。若未上锁则先给它上锁 P(mutex); if (count == 0) P(rw); count++; V(mutex); V(w); // 读进程加入队列之后,将w锁解锁,保证加入队列操作的原子性。 读文件; P(mutex); count--; if (count == 0) V(rw); V(mutex); } } ``` semaphore rw = 1; int count = 0; semaphore mutex = 1; semephore w = 1; // 用于实现写优先 writer () { while (1) { P(w); // 写进程先给w锁上锁,上锁后后来的读进程都需等待这个写进程解锁。 P(rw); // 给缓冲区上锁 写文件; V(rw); V(w); // 解锁,允许读进程加入读队列。 } } reader () { while (1) { P(w); // 新的读进程加入时先检查w锁是否上锁,若已上锁则阻塞等待。若未上锁则先给它上锁 P(mutex); if (count == 0) P(rw); count++; V(mutex); V(w); // 读进程加入队列之后,将w锁解锁,保证加入队列操作的原子性。 读文件; P(mutex); count--; if (count == 0) V(rw); V(mutex); } } ``` 加入了一个新的锁`w`之后,大大提升了写进程的优先级,实现了**先来先服务**。