操作系统实验一:读者 — 写者问题
2022年10月12日
问题描述
读者 — 写者问题是经典的多进程问题,其中包含了互斥、上锁、解锁等方面的思想运用。其本质就是保证一个 Writer 进程必须与其他进程互斥地访问共享对象的同步问题。读者 — 写者问题常被用来测试新同步原语。
问题要求
操作限制
- 写 — 写互斥:不能同时有两个写者同时写;
- 读 — 写互斥:不能同时有一个线程在读,一个线程在写;
- 读 — 读允许:允许同时有两个读者在读。
如果读者来:
- 没有读者以及写者,新读者可以读;
- 有写者等待,但有其他读者正在读,新读者也可以读;
- 有写者正在写,新读者需等待。
如果写者来:
- 没有读者,新写者可以写;
- 有读者,新写者等待;
- 有其他写者,新写者等待。
读者优先原则:如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。
写者优先原则:如果一个读者申请进行读操作时已有另一写者在等待访问共享资源,则该读者必须等到没有写者处于等待状态后才能开始读操作。
问题分析
读者优先和写者优先的区别就在于在有读者正在读,有写者等待的情况下,新读者如何处理。在读者优先中遇到这样的情况时,新读者可以直接进行读操作,而不需要进行等待;而在写者优先中新读者需要等待所有的写者都完成写操作之后才能进行读取。弄明白这个,整个代码的思路也就清楚了。剩下的难题就是学会如何使用编程语言中有关 “进程” 的方法或者函数,从而让之完成我们需要的任务。
实验用例
用例意义顺序为:编号、身份、开始时间、持续时间
[1, 'W', 3, 5],[2, 'W', 16, 5],[3, 'R', 5, 2],[4, 'W', 6, 5],[5, 'R', 4, 3],[6, 'R', 11, 4]
实验过程
在我们知道并且理解 读者 — 写者问题之后,再根据测试样例,我们可以先用人脑走一遍整个流程:
程序开始
第 1 秒
第 2 秒
第 3 秒:创建一个线程,编号为 1 的写者申请写操作,编号为 1 的写者开始写操作
第 4 秒:创建一个线程,编号为 5 的读者申请读操作
第 5 秒:创建一个线程,编号为 3 的读者申请读操作
第 6 秒:创建一个线程,编号为 4 的写者申请写操作
第 7 秒
第 8 秒:编号为 1 的写者完成写操作,编号为 5 的读者和编号为 3 的读者开始读操作
第 9 秒
第 10 秒:编号为 3 的读者完成读操作
第 11 秒:编号为 5 的读者完成读操作,编号为 4 的写者开始写操作,创建一个线程,编号为 6 的读者申请读操作
第 12 秒
第 13 秒
第 14 秒
第 15 秒
第 16 秒:编号为 4 的写者完成写操作,编号为 6 的读者开始读操作,创建一个线程,编号为 2 的写者申请写操作
第 17 秒
第 18 秒
第 19 秒
第 20 秒:编号为 6 的读者完成读操作,编号为 2 的写者开始写操作
第 21 秒
第 22 秒
第 23 秒
第 24 秒
第 25 秒:编号为 2 的写者完成写操作
程序结束
源代码
# 操作系统课程的所有实验代码都是使用 python 来进行编码
import time
import threading
from threading import Semaphore
RCounter = 0 # 读者数量
Wmutex = Semaphore(1) # 读者锁
Rmutex = Semaphore(1) # 写者互斥变量
def reader(duration, No): # 定义读者
print('读者 ' + str(No) + ' 申请读操作\n', end = '')
Rmutex.acquire() # 互斥读者
global RCounter
if RCounter == 0:
Wmutex.acquire() # 如果是第一个读者,就上写者锁
RCounter += 1
Rmutex.release() # 释放互斥变量
print('读者 ' + str(No) + ' 开始读\n', end = '')
time.sleep(duration*2) # 进行读操作
print('读者 ' + str(No) + ' 完成读操作\n', end = '')
Rmutex.acquire() # 互斥读者
RCounter -= 1
if RCounter == 0:
Wmutex.release() # 如果是最后一个读者,就释放写者锁
Rmutex.release() # 释放互斥变量
def writer(duration, No): # 定义写者
print('写者 ' + str(No) + ' 申请写操作\n', end = '')
Wmutex.acquire() # 上写者锁
print('写者 ' + str(No) + ' 开始写\n', end = '')
time.sleep(duration*2) #进行写操作
print('写者 ' + str(No) + ' 完成写操作\n', end = '')
Wmutex.release() # 释放写者锁
# 定义线程 序号、读/写者身份、开始时间、持续时间
List = [[1, 'W', 3, 5], [2, 'W', 16, 5], [3, 'R', 5, 2], [4, 'W', 6, 5], [5, 'R', 4, 3], [6, 'R', 11, 4]]
if __name__ == '__main__':
print(List)
print('------开始------')
for i in range(60):
if i % 2 == 0: # 以两秒作为一个时间片
print('------' + str(i/2) + '------')
for t in List:
if t[2] * 2 + 1 == i:
if t[1] == 'W':
T = threading.Thread(target = writer, args = (t[3], t[0]))
else:
T = threading.Thread(target = reader, args = (t[3], t[0]))
print('线程 ' + str(t[0]) + ' 创建')
T.start()
time.sleep(1)
print('------结束------')