前言
这是我参与「第四届青训营 」笔记创作活动的第19天,在系统学习了「深入浅出 HBase 实战」、「数据湖三剑客:Delta Lake、Hudi 与 Iceberg 详解」、「从 Kafka 到 Pulsar:数据流演进之路」、「Parquet 与 ORC:高性能列式存储」和「LSMT 存储引擎浅析」这几门课程后,我发现这些课程都不约而同提到了读写问题,尤其是写入问题。主要就是集中在写写冲突,写读冲突上面。而读者写者冲突问题恰好又是《操作系统》这门关键课程的核心知识点之一,于是我想借此机会再次梳理、阐述一下读写问题的原理以及一些解决思想,也可以帮助重新回顾《操作系统》课程中的部分进程管理知识。
读写问题描述
在某操作系统中,假设同时存在读者和写者两组并发进程共享某个文件,当两个或两个以上的读进程同时访问共享文件时,不产生任何冲突。但如果某个写进程和其他任意写进程或读进程同时访问该共享文件时,可能会导致数据不一致的错误。因此,为了避免这种错误,我们要求:
- 允许多个读者对该文件进行读操作;
- 某个时刻只允许一个写者往该文件中写数据;
- 任一写者在完成写操作前不允许其他读者或写者对该文件进行操作;
- 写者进行写操作前,其他读者和写者进程应该全部退出。
读写问题分析
(1)读写关系分析
通过上一小节读写问题描述,我们可以分析发现,读者和写者之间是互斥关系、写者和写者之间也是互斥关系、而读者和读者之间不存在互斥关系。
(2)解决思路整理
显然,写者和其他任何读写进程都是互斥的,需要用互斥关系来解决。而读者在保证和写者互斥的同时,还需要保证和其他读者的同步问题,因此较为复杂,无法用简单的互斥关系来解决。因此,我们需要设定一个计数器,用计数器来表示当前是否有读者在对文件进行读操作,当计数器为0时,写者才可以对文件进行写操作,同时,不同读者对该计数器的访问也应该是互斥的,否则会导致计数器的计数混乱。
读写问题解决
(1)PV代码展示
int count = 0; //记录当前读者数量
semaphore mutex = 1; //保护count变量,用于互斥
semaphore rw = 1; //读写互斥,用来互斥访问共享文件
writer(){
while(1){
P(rw); //互斥访问共享文件
writing;
V(rw); //释放共享文件
}
}
reader(){
while(1){
P(mutex); //互斥访问count变量
if(count == 0) //第一个读进程访问共享文件
P(rw); //阻止写进程
count++; //读者+1
V(mutex); //释放互斥变量count
reading;
P(mutex); //互斥访问count变量
count--;; //读者-1
if(count == 0)
V(rw); //允许写进程写
V(mutex); //允许后续读者进程修改count变量
}
}
总结分析
显然,在该算法中,读进程是优先的,因为读进程存在时,写进程将被阻止延迟,同时,只要有一个读进程活跃,其他后来的读进程都将被允许访问该共享文件。这样有可能会导致写进程长时间等待而被“饿死”。