实 验 题 目 进程同步与互斥:读者-写者问题
摘 要 本次实验实现多线程编程和信号量控制,实现了读者-写者问题的解决方案,并通过多线程模拟了多个读者和写者并发访问共享资源的过程。代码的主要逻辑:读取输入数据,初始化信号量和全局变量,启动主线程,创建并启动线程,模拟读者写者行为,并发执行和输出。
一、需求分析
创建一个控制台进程,此进程包含 n 个线程。用这 n 个线程来表示 n个读者或写者。每个线程按相应测试数据文件的要求进行读写操作。要实现写-写互斥,读-写互斥,读-读允许。如果一个读者申请进行读操作时已有另一个读者正在进行读操作,则该读者可直接开始读操作。但任何写者必须等到没有读者时才能开始写操作。
运行结果要求在每个线程创建、发出读写操作申请、开始读写操作和结束读写操作时分别显示一行提示信息,以确定所有处理都遵守相应的读写操作限制。
二、概要设计
- 系统概述
本系统是一个多线程模拟程序,旨在实现读者-写者问题的解决方案。通过创建多个线程来模拟读者和写者对共享资源的并发访问,利用信号量机制控制对共享资源的访问权限,确保读者和写者之间的正确互斥和同步。
- 模块
- 输入模块:负责从指定的文本文件中读取数据,解析出读者和写者的线程编号、操作类型、开始时间和持续时间,并将这些数据存储到相应的列表中。
- 线程创建与启动模块:根据输入模块提供的数据,为每个读者或写者创建一个线程,并设置相应的参数。然后启动这些线程,让它们并发执行。
- 读者模块:模拟读者的行为。读者线程在启动后会等待一段时间,然后尝试获取读取权限。如果当前没有其他读者正在读取,读者会获取写者互斥信号量Wmutex,确保写者不会进行写操作。接着,读者会更新读者计数Rcount,并开始读取操作。读取完成后,读者会释放写者互斥信号量,并更新读者计数。
- 写者模块:模拟写者的行为。写者线程在启动后会等待一段时间,然后尝试获取写权限。写者会获取写者互斥信号量Wmutex,确保独占写权限。然后,写者进行写操作。写操作完成后,写者会释放写者互斥信号量。
- 输出模块:负责在关键的时间点打印出线程的执行状态,包括线程编号、操作类型、开始时间和持续时间等信息。通过输出模块,可以观察多线程的执行过程和资源访问情况。
- 数据结构
- idx:存储每个线程的编号。
- rwlist:存储每个线程的操作类型,包括读者和写者
- start_time:存储每个线程开始等待的时间。
- continue_time:存储每个线程的操作持续时间。
- Semaphore:使用两个信号量Wmutex和Rmutex来控制对共享资源的访问。Wmutex用于保护写操作,确保同一时间只有一个写者可以访问共享资源;Rmutex用于控制读者的并发访问。
- Rcount:用于记录当前并发读者的数量,实现读者之间的互斥和同步。
三、详细设计
- 系统初始化:初始化信号量Wmutex和Rmutex为1,初始时没有线程在执行写操作或者等待读取。初始化全局变量Rcount为0,表示当前没有读者正在读取。
- 输入模块:打开input.txt,逐行读取解析数据,提取线程编号、操作类型、开始时间和持续时间,并将数据存储到idx、rwlist、start_time和continue_time列表中,最后关闭文本文件。遍历rwlist列表,根据每个元素的操作类型创建相应的线程。对于读者线程,将线程编号、持续时间、全局开始时间以及等待开始时间作为参数传递给reader函数。对于写者线程,同样将线程编号、持续时间、全局开始时间以及等待开始时间作为参数传递给writer函数。使用threading.Thread类创建线程对象,并调用其start方法启动线程。
- 读者模块:读者线程启动后,首先根据等待开始时间进行延时,模拟线程等待的过程,延时结束后,读者尝试获取Rmutex信号量,以确保在修改Rcount时的线程安全获取到Rmutex后,读者检查Rcount是否为0,如果是,则获取Wmutex信号量,以确保写者不会进行写操作,读者增加Rcount的值,表示有一个新的读者开始读取。释放Rmutex信号量,允许其他读者进入。读者开始执行读取操作,模拟读取过程。读取操作完成后,读者再次获取Rmutex信号量,准备退出。减少Rcount的值,表示有一个读者完成读取。如果Rcount变为0,表示所有读者都已完成读取,释放Wmutex信号量,允许写者进行写操作。释放Rmutex信号量,读者线程结束。
- 写者模块:写者线程启动后,根据等待开始时间进行延时。延时结束后,写者尝试获取Wmutex信号量,以确保独占写权限,如果成功获取到Wmutex信号量,写者开始执行写操作,模拟写过程。写操作完成后,写者释放Wmutex信号量,允许其他读者或写者获取写权限,最后写者线程结束。
- 输出模块:在相关时间点,通过打印语句输出线程的执行状态。输出内容包括线程编号、操作类型、当前时间以及持续时间等信息
四、调试分析
- 输出检查:查看程序输出的线程状态信息,每个线程都按照预期的时间点和顺序执行。
- 时间分析:分析线程的执行时间,不存在不必要的等待或延迟。
- 信号量状态分析:检查信号量的获取和释放情况,信号量的状态与线程的行为一致。
- 逻辑检查:代码逻辑符合读者-写者问题的要求,读者和写者的行为符合预期。
- 语法检查:代码中没有语法错误
- 变量参数检查:所有变量和参数都已正确初始化
五、用户使用说明
- 运行环境(操作系统:Windows;Python 版本:Python 3.x 版本;依赖库: Python 标准库)
- 准备输入文件input.txt
1 R 3 5
2 R 4 5
3 W 5 2
4 R 10 3
5 W 11 3
6 R 13 4
3.运行
4.结果输出
六、测试与运行结果
源代码:
import time
import threading
from threading import Semaphore
Wmutex = Semaphore(1)
Rmutex = Semaphore(1)
Rcount = 0
def reader(i,sleept,start,start_sleep):
time.sleep(start_sleep)
print('时间点 '+str(int(time.strftime('%S'))-start)+' reader '+str(i)+' waiting to read\n', end='')
Rmutex.acquire()
global Rcount
if Rcount == 0:
Wmutex.acquire()
Rcount += 1
Rmutex.release()
print('时间点 '+str(int(time.strftime('%S'))-start)+' reader '+str(i)+' reading\n', end='')
time.sleep(sleept)
print('时间点 '+str(int(time.strftime('%S'))-start)+' reader '+str(i)+' finish reading\n', end='')
Rmutex.acquire()
Rcount -= 1
if Rcount == 0:
Wmutex.release()
Rmutex.release()
def writer(i,sleept,start,start_sleep):
time.sleep(start_sleep)
now = int(time.strftime('%S'))
print('时间点 '+str(int(time.strftime('%S'))-start)+' writer '+str(i)+' waiting to write\n', end='')
Wmutex.acquire()
print('时间点 '+str(int(time.strftime('%S'))-start)+' writer '+str(i)+' writing\n', end='')
time.sleep(sleept)
print('时间点 '+str(int(time.strftime('%S'))-start)+' writer '+str(i)+' finish writing\n', end='')
Wmutex.release()
if __name__ == '__main__':
idx = []
rwlist = []
start_time = []
continue_time = []
with open(r'C:\Users\JH\Desktop\input.txt','r',encoding='utf-8') as f:
data = f.readlines()
for x in data:
x = x.split()
idx.append(int(x[0]))
rwlist.append(x[1])
start_time.append(int(x[2]))
continue_time.append(int(x[3]))
start = int(time.strftime('%S'))
print('时间点 '+str(start-start)+' 所有线程开始启动\n', end='')
for i in range(len(rwlist)):
print('时间点 '+str(int(time.strftime('%S'))-start)+' 线程 '+str(idx[i])+' set up\n', end='')
if rwlist[i] == 'R':
t = threading.Thread(target=reader, args=(idx[i],continue_time[i],start,start_time[i]))
t.start()
else:
t = threading.Thread(target=writer, args=(idx[i],continue_time[i],start,start_time[i]))
t.start()