怎么样用 Python 实现读写锁
起步
Python 提供的多線程模型中并沒有提供讀寫鎖,讀寫鎖相對于單純的互斥鎖,適用性更高,可以多個線程同時占用讀模式的讀寫鎖,但是只能一個線程占用寫模式的讀寫鎖。
通俗點說就是當沒有寫鎖時,就可以加讀鎖且任意線程可以同時加;而寫鎖只能有一個線程,且必須在沒有讀鎖時才能加上。
簡單的實現
import?threadingclass?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://www.pinlue.com/
轉載于:https://blog.51cto.com/14325182/2406001
總結
以上是生活随笔為你收集整理的怎么样用 Python 实现读写锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装和规划邮件服务器
- 下一篇: CF981H K Paths