Python 提供的多線程模型中並無提供讀寫鎖,讀寫鎖相對於單純的互斥鎖,適用性更高,能夠多個線程同時佔用讀模式的讀寫鎖,可是隻能一個線程佔用寫模式的讀寫鎖。多線程
通俗點說就是當沒有寫鎖時,就能夠加讀鎖且任意線程能夠同時加;而寫鎖只能有一個線程,且必須在沒有讀鎖時才能加上。app
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
。ide
可是這個鎖是不公平的。理想狀況下,線程得到所的機會應該是同樣的,無論線程是讀操做仍是寫操做。而從上述代碼能夠看到,讀請求都會當即設置 self.read_num += 1
,無論有沒有得到鎖,而寫請求想要得到鎖還得等待 read_num
爲 0 。ui
因此這個就形成了只有鎖沒有被佔用或者沒有讀請求時,能夠得到寫權限。咱們應該想辦法避免讀模式鎖長期佔用。線程
讀寫鎖也有分 讀優先 和 寫優先。上面的代碼就屬於讀優先。code
若是要改爲寫優先,那就換成去記錄寫線程的引用計數,讀和寫在同時競爭時,可讓寫線程增長寫的計數,這樣可以使讀線程的讀鎖一直獲取不到, 由於讀線程要先判斷寫的引用計數,若不爲0,則等待其爲 0,而後進行讀。這部分代碼不羅列了。rem
但這樣顯然不夠靈活。咱們不須要兩個類似的讀寫鎖類。咱們但願重構咱們代碼,使它更強大。get
爲了可以知足自定義優先級的讀寫鎖,要記錄等待的讀寫線程數,而且須要兩個條件 threading.Condition
用來處理哪方優先的通知。計數引用能夠擴大語義:正數:表示正在讀操做的線程數,負數:表示正在寫操做的線程數(最多-1)it
在獲取讀操做時,先而後判斷時候有等待的寫線程,沒有,進行讀操做,有,則等待讀的計數加 1 後等待 Condition 通知;等待讀的計數減 1,計數引用加 1,繼續讀操做,若條件不成立,循環等待;io
在獲取寫操做時,若鎖沒有被佔用,引用計數減 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