作者:weapon,閑來笑浮生懸筆一卷入毫端,朱紱臨身可與言者不過二三。
博客: zhihu.com/people/hong-wei-peng
起步
Python 提供的多線程模型中并沒有提供讀寫鎖,讀寫鎖相對(duì)于單純的互斥鎖,適用性更高,可以多個(gè)線程同時(shí)占用讀模式的讀寫鎖,但是只能一個(gè)線程占用寫模式的讀寫鎖。
通俗點(diǎn)說就是當(dāng)沒有寫鎖時(shí),就可以加讀鎖且任意線程可以同時(shí)加;而寫鎖只能有一個(gè)線程,且必須在沒有讀鎖時(shí)才能加上。
簡(jiǎn)單的實(shí)現(xiàn)
這是讀寫鎖的一個(gè)簡(jiǎn)單的實(shí)現(xiàn),
self.read_num
?用來保存獲得讀鎖的線程數(shù),這個(gè)屬性屬于臨界區(qū),對(duì)其操作也要加鎖,所以這里需要一個(gè)保護(hù)內(nèi)部數(shù)據(jù)的額外的鎖?
self._extra
?。
但是這個(gè)鎖是不公平的。理想情況下,線程獲得所的機(jī)會(huì)應(yīng)該是一樣的,不管線程是讀操作還是寫操作。而從上述代碼可以看到,讀請(qǐng)求都會(huì)立即設(shè)置?
self.read_num += 1
,不管有沒有獲得鎖,而寫請(qǐng)求想要獲得鎖還得等待?
read_num
為 0 。
所以這個(gè)就造成了只有鎖沒有被占用或者沒有讀請(qǐng)求時(shí),可以獲得寫權(quán)限。我們應(yīng)該想辦法避免讀模式鎖長(zhǎng)期占用。
讀寫鎖的優(yōu)先級(jí)
讀寫鎖也有分 讀優(yōu)先 和 寫優(yōu)先。上面的代碼就屬于讀優(yōu)先。
如果要改成寫優(yōu)先,那就換成去記錄寫線程的引用計(jì)數(shù),讀和寫在同時(shí)競(jìng)爭(zhēng)時(shí),可以讓寫線程增加寫的計(jì)數(shù),這樣可使讀線程的讀鎖一直獲取不到, 因?yàn)樽x線程要先判斷寫的引用計(jì)數(shù),若不為0,則等待其為 0,然后進(jìn)行讀。這部分代碼不羅列了。
但這樣顯然不夠靈活。我們不需要兩個(gè)相似的讀寫鎖類。我們希望重構(gòu)我們代碼,使它更強(qiáng)大。
改進(jìn)
為了能夠滿足自定義優(yōu)先級(jí)的讀寫鎖,要記錄等待的讀寫線程數(shù),并且需要兩個(gè)條件
threading.Condition
?用來處理哪方優(yōu)先的通知。計(jì)數(shù)引用可以擴(kuò)大語義:正數(shù):表示正在讀操作的線程數(shù),負(fù)數(shù):表示正在寫操作的線程數(shù)(最多-1)
在獲取讀操作時(shí),先然后判斷時(shí)候有等待的寫線程,沒有,進(jìn)行讀操作,有,則等待讀的計(jì)數(shù)加 1 后等待?
Condition
?通知;等待讀的計(jì)數(shù)減 1,計(jì)數(shù)引用加 1,繼續(xù)讀操作,若條件不成立,循環(huán)等待;
在獲取寫操作時(shí),若鎖沒有被占用,引用計(jì)數(shù)減 1,若被占用,等待寫線程數(shù)加 1,等待寫條件?
Condition
?的通知。
讀模式和寫模式的釋放都是一樣,需要根據(jù)判斷去通知對(duì)應(yīng)的?
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
????
#?等待獲取讀鎖的線程數(shù)
????????
self
.write_waiter?=?
0
???
#?等待獲取寫鎖的線程數(shù)
????????
self
.state?=?
0
??????????
#?正數(shù):表示正在讀操作的線程數(shù)???負(fù)數(shù):表示正在寫操作的線程數(shù)(最多-1)
????????
self
.owners?=?[]????????
#?正在操作的線程id集合
????????
self
.write_first?=?True?
#?默認(rèn)寫優(yōu)先,F(xiàn)alse表示讀優(yōu)先
????
def
?
write_acquire(
self
,?blocking=True)
:
????????
#?獲取寫鎖只有當(dāng)
????????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)
:
????????
#?獲取寫鎖只有當(dāng)鎖沒人占用,或者當(dāng)前線程已經(jīng)占用
????????
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:
???
#?如果有寫操作在等待(默認(rèn)寫優(yōu)先)
????????????????????
self
.wcond.notify()
????????????????elif?
self
.
read_waiter:
????????????????????
self
.rcond.notify_all()
????????????????elif?
self
.
write_waiter:
????????????????????
self
.wcond.notify()
????read_release?=?unlock
????write_release?=?unlock
用Python創(chuàng)建微信機(jī)器人
用Python機(jī)器人監(jiān)聽微信群聊
用Python獲取攝像頭并實(shí)時(shí)控制人臉
開源項(xiàng)目 | 用Python美化LeetCode倉庫
推薦Python中文社區(qū)旗下的幾個(gè)服務(wù)類公眾號(hào)
征稿啟事 | Python中文社區(qū)有獎(jiǎng)?wù)魑?
▼ 點(diǎn)擊成為 社區(qū)注冊(cè)會(huì)員? ? ? ? ?? 「在看」 一下,一起PY!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
