題目描述
解決讀者-寫者問題,僅要求解決讀者優先的情況。
提示:創建一個控制檯進程,此進程包含 n 個線程。用這 n 個線程來表示 n 個讀者或寫
者。每個線程按相應測試數據文件的要求進行讀寫操作。
讀者-寫者問題的讀寫操作限制:
1)寫-寫互斥,即不能有兩個寫者同時進行寫操作。
2)讀-寫互斥,即不能同時有一個線程在讀,而另一個線程在寫。
3)讀-讀允許,即可以有一個或多個讀者在讀。
讀者優先的附加限制:
如果一個讀者申請進行讀操作時已有另一個讀者正在進行讀操
作,則該讀者可直接開始讀操作。但任何寫者必須等到沒有讀者時才能開始寫操作。
運行結果顯示要求:
要求在每個線程創建、發出讀寫操作申請、開始讀寫操作和結束讀寫操作時分別顯示一行提示信息,以確定所有處理都遵守相應的讀寫操作限制。
測試數據文件包括 n 行測試數據,分別描述創建的 n 個線程是讀者還是寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括四個字段,各個字段間用空格分隔。第一字段爲一個正整數,表示線程序號。第二字段表示相應線程角色,R 表示讀者,W 表示寫者。第三字段爲一個正數,表示讀寫操作的開始時間:線程創建後,延遲相應時間(單位爲秒)後發出對共享資源的讀寫申請。第四字段爲一個正數,表示讀寫操作的持續時間。當線程讀寫申請成功後,開始對共享資源的讀寫操作,該操作持續相應時間後結束,並釋放共享資源。
測試數據隨機生成,爲簡化起見,假設每個線程只執行一次讀或寫操作,之後運行結束。
下面是一個測試數據文件的例子:
線程序號 角色 何時開始讀寫 讀寫持續時間
1 R 3 5
2 R 4 5
3 W 5 2
4 R 10 3
5 W 11 3
6 R 13 4
…… …… ……
輸入的形式和輸入值的範圍
測試數據使用文件來保存,在我的程序中命名爲“input.txt”。測試數據文件包括 n 行測試數據,分別描述創建的n個線程是讀者還是寫者,以及讀寫操作的開始時間和持續時間。每行測試數據包括四個字段,各個字段間用空格分隔。第一字段爲一個正整數,表示線程序號。第二字段表示相應線程角色,R 表示讀者,W 表示寫者。第三字段爲一個正數,表示讀寫操作的開始時間:線程創建後,延遲相應時間(單位爲秒)後發出對共享資源的讀寫申請。第四字段爲一個正數,表示讀寫操作的持續時間。
輸入文件 input.txt
輸出結果
主要包含包
(1)threading模塊
threading模塊是在低級別_thread模塊上構建的的高級別線程接口。繼承_thread功能,而_thread模塊是提供處理多進程(也稱輕量級繼承或任務)的基本單元,多進程控制特點是共享全局數據空間.簡單鎖(也稱互斥或二進制信號量)可實現進程同步。
python中線程屬於內核級別,即由操作系統調度(如單線程一旦遇到IO就會被迫交出CPU執行權限,切換到其他線程運行).
(2)Thread類
線程類表示單獨控制運行的線程活動.有兩種建立線程的方式:
• 直接使用Thread類;(本文使用)
• 重構run()方法.
(3)Semaphore類
Semaphore 在內部管理着一個計數器。調用 acquire() 會使這個計數器 -1,release() 則是+1.計數器的值永遠不會小於 0,當計數器到 0 時,再調用 acquire() 就會阻塞,直到其他線程來調用release()
(4)time類
用於獲取當前時間,精確到秒,從而與最開始時間相減來獲得時間點
程序流程圖
各模塊間層次
讀者優先的特點是當讀者進行讀時,後續的寫者必須等待,直到所有的讀者均離開後,寫者纔可進入。
讀者優先問題的關鍵互斥問題是解決“寫者與寫者”和“寫者與第一個讀者”的互斥問題,爲此引入互斥信號量Wmutex。爲了記錄誰是第一個讀者,用一個全局整型變量Rcount做一個計數器。而在解決問題的過程中,由於使用了全局變量Rcount,該變量又是一個臨界資源,對於他的訪問仍需互斥進行,所以需要一個互斥信號量Rmutex。
-
reader函數
用於讀者的操作。最開始創建後sleep到開始時間,然後進入等待隊列請求讀操作。請求到之後如果Rcount=0(自己是第一個讀者)則把寫者的信號量申請,不讓寫者進行寫操作,然後Rcount+1,然後釋放Rmutex(讓後續讀進程可以一起申請讀操作)。最關鍵的部分就是Rcount來記錄第一個讀者,實現與寫者的互斥,而這通過互斥信號量Rmutex來修來Rcount. -
writer函數
用於寫者的操作,最開始創建後sleep到開始時間,然後進入等待隊列請求寫操作。然後申請Wmutex信號量,申請到了就可以進行寫,最後再釋放。 -
四個list
idx = [] 存取線程序號
rwlist = [] 存取線程類型
start_time = [] 存取線程開始時間
continue_time = [] 存取線程持續時間 -
線程啓動函數
-
主函數
再來一個圖理解
需要的環境
1.安裝python3
2.確保有time threading 兩個模塊(沒有則在命令行運行pip install [模塊名])
3.運行python Reader_first.py
4.也可以使用jupyter打開Reader_first.ipynb 可以看到相關輸出
5.測試數據在input.txt中修改
代碼
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('input.txt','r') 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()
最後 使用python寫操作系統實驗只需要幾十行代碼 比起用java或者c語言寫上百行 不快樂嗎
給出參考博客