摘要:讀寫鎖的優先級讀寫鎖也有分讀優先和寫優先。我們不需要兩個相似的讀寫鎖類。改進為了能夠滿足自定義優先級的讀寫鎖,要記錄等待的讀寫線程數,并且需要兩個條件用來處理哪方優先的通知。
起步
Python 提供的多線程模型中并沒有提供讀寫鎖,讀寫鎖相對于單純的互斥鎖,適用性更高,可以多個線程同時占用讀模式的讀寫鎖,但是只能一個線程占用寫模式的讀寫鎖。
通俗點說就是當沒有寫鎖時,就可以加讀鎖且任意線程可以同時加;而寫鎖只能有一個線程,且必須在沒有讀鎖時才能加上。
簡單的實現import threading class RWlock(object): def __init__(self): self._lock = threading.Lock() self._extra = threading.Lock() self.read_num = 0 def read_acquire(self): with self._extra: self.read_num += 1 if self.read_num == 1: self._lock.acquire() def read_release(self): with self._extra: self.read_num -= 1 if self.read_num == 0: self._lock.release() def write_acquire(self): self._lock.acquire() def write_release(self): self._lock.release()
這是讀寫鎖的一個簡單的實現,self.read_num 用來保存獲得讀鎖的線程數,這個屬性屬于臨界區,對其操作也要加鎖,所以這里需要一個保護內部數據的額外的鎖 self._extra 。
但是這個鎖是不公平的。理想情況下,線程獲得所的機會應該是一樣的,不管線程是讀操作還是寫操作。而從上述代碼可以看到,讀請求都會立即設置 self.read_num += 1,不管有沒有獲得鎖,而寫請求想要獲得鎖還得等待 read_num 為 0 。
所以這個就造成了只有鎖沒有被占用或者沒有讀請求時,可以獲得寫權限。我們應該想辦法避免讀模式鎖長期占用。
讀寫鎖的優先級讀寫鎖也有分 讀優先 和 寫優先。上面的代碼就屬于讀優先。
如果要改成寫優先,那就換成去記錄寫線程的引用計數,讀和寫在同時競爭時,可以讓寫線程增加寫的計數,這樣可使讀線程的讀鎖一直獲取不到, 因為讀線程要先判斷寫的引用計數,若不為0,則等待其為 0,然后進行讀。這部分代碼不羅列了。
但這樣顯然不夠靈活。我們不需要兩個相似的讀寫鎖類。我們希望重構我們代碼,使它更強大。
改進為了能夠滿足自定義優先級的讀寫鎖,要記錄等待的讀寫線程數,并且需要兩個條件 threading.Condition 用來處理哪方優先的通知。計數引用可以擴大語義:正數:表示正在讀操作的線程數,負數:表示正在寫操作的線程數(最多-1)
在獲取讀操作時,先然后判斷時候有等待的寫線程,沒有,進行讀操作,有,則等待讀的計數加 1 后等待 Condition 通知;等待讀的計數減 1,計數引用加 1,繼續讀操作,若條件不成立,循環等待;
在獲取寫操作時,若鎖沒有被占用,引用計數減 1,若被占用,等待寫線程數加 1,等待寫條件 Condition 的通知。
讀模式和寫模式的釋放都是一樣,需要根據判斷去通知對應的 Condition:
class RWLock(object): def __init__(self): self.lock = threading.Lock() self.rcond = threading.Condition(self.lock) self.wcond = threading.Condition(self.lock) self.read_waiter = 0 # 等待獲取讀鎖的線程數 self.write_waiter = 0 # 等待獲取寫鎖的線程數 self.state = 0 # 正數:表示正在讀操作的線程數 負數:表示正在寫操作的線程數(最多-1) self.owners = [] # 正在操作的線程id集合 self.write_first = True # 默認寫優先,False表示讀優先 def write_acquire(self, blocking=True): # 獲取寫鎖只有當 me = threading.get_ident() with self.lock: while not self._write_acquire(me): if not blocking: return False self.write_waiter += 1 self.wcond.wait() self.write_waiter -= 1 return True def _write_acquire(self, me): # 獲取寫鎖只有當鎖沒人占用,或者當前線程已經占用 if self.state == 0 or (self.state < 0 and me in self.owners): self.state -= 1 self.owners.append(me) return True if self.state > 0 and me in self.owners: raise RuntimeError("cannot recursively wrlock a rdlocked lock") return False def read_acquire(self, blocking=True): me = threading.get_ident() with self.lock: while not self._read_acquire(me): if not blocking: return False self.read_waiter += 1 self.rcond.wait() self.read_waiter -= 1 return True def _read_acquire(self, me): if self.state < 0: # 如果鎖被寫鎖占用 return False if not self.write_waiter: ok = True else: ok = me in self.owners if ok or not self.write_first: self.state += 1 self.owners.append(me) return True return False def unlock(self): me = threading.get_ident() with self.lock: try: self.owners.remove(me) except ValueError: raise RuntimeError("cannot release un-acquired lock") if self.state > 0: self.state -= 1 else: self.state += 1 if not self.state: if self.write_waiter and self.write_first: # 如果有寫操作在等待(默認寫優先) self.wcond.notify() elif self.read_waiter: self.rcond.notify_all() elif self.write_waiter: self.wcond.notify() read_release = unlock write_release = unlock
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/44940.html
摘要:我們知道多線程環境下,每一個線程均可以使用所屬進程的全局變量。在線程中使用局部變量則不存在這個問題,因為每個線程的局部變量不能被其他線程訪問。 我們知道多線程環境下,每一個線程均可以使用所屬進程的全局變量。如果一個線程對全局變量進行了修改,將會影響到其他所有的線程。為了避免多個線程同時對變量進行修改,引入了線程同步機制,通過互斥鎖,條件變量或者讀寫鎖來控制對全局變量的訪問。 只用全局變...
摘要:操作系統實戰臨界資源保護臨界資源進行通信線程間同步互斥量和讀寫鎖自旋鎖條件變量進程間同步共享內存域套接字重要概念用戶態與內核態上下文切換協程線程同步之互斥量互斥量鎖可以保證多線程的指令按順序執行,避免兩個線程的指令交叉執行即原子性原子性是指 操作系統實戰 臨界資源 保護臨界資源/進行通信 線程間同步 互斥量和 讀寫鎖 自旋鎖 條件變量 進程間同步 共享內存 域套接字 重要概念 用...
摘要:鎖實現分析本節通過學習源碼分析可重入讀寫鎖的實現。讀寫鎖結構分析繼承于,其中主要功能均在中完成,其中最重要功能為控制線程獲取鎖失敗后轉換為等待狀態及在滿足一定條件后喚醒等待狀態的線程。 概述 本文主要分析JCU包中讀寫鎖接口(ReadWriteLock)的重要實現類ReentrantReadWriteLock。主要實現讀共享,寫互斥功能,對比單純的互斥鎖在共享資源使用場景為頻繁讀取及少...
閱讀 1239·2021-11-11 16:55
閱讀 1537·2021-10-08 10:16
閱讀 1188·2021-09-26 10:20
閱讀 3569·2021-09-01 10:47
閱讀 2451·2019-08-30 15:52
閱讀 2682·2019-08-30 13:18
閱讀 3194·2019-08-30 13:15
閱讀 1115·2019-08-30 10:55