python多線程鎖Lock和RLock

如果多個(gè)線程共同對某個(gè)數(shù)據(jù)修改,則可能出現(xiàn)不可預(yù)料的結(jié)果睁本,為了保證數(shù)據(jù)的正確性,需要對多個(gè)線程進(jìn)行同步,使用 Thread 對象的 Lock 和 Rlock 可以實(shí)現(xiàn)簡單的線程同步忠怖,這兩個(gè)對象都有 acquire 方法和 release 方法,分別用來獲取和釋放鎖

Lock和RLock有什么區(qū)別呢呢堰?很多教程都只提到RLock可以多次acquire和release,舉的例子就是類似這種:

import threading

rlock=threading.RLock()

def foo():
    rlock.acquire()
    rlock.acquire() # 可以多次獲得鎖
    print('multi-acquire')
    rlock.release()
    rlock.acquire() # acquire幾次鎖就要release 幾次

t=threading.Thread(target=foo)
t.start()
t.join()

#輸出
multi-acquire

看完了還是一頭霧水脑又,雖然知道了RLock可以多次獲取釋放暮胧,但有什么用呢?獲取一次鎖就行了為啥還要多次獲取呢问麸? 下面就舉例說一下這個(gè)RLock可以多次獲取鎖的好處

假如在多線程環(huán)境下我們要對一個(gè)變量num進(jìn)行加一操作往衷,并且加一操作前我們需要檢查這個(gè)變量是不是負(fù)值

import threading

lock=threading.Lock()
num=1

def check(): #檢查變量是否小于零
    global num
    lock.acquire()
    if num<0:
        print('num<0')
    else:
        print('num>1')
    lock.release()

def add(): # 對變量進(jìn)行+1操作
    global num
    lock.acquire()
    check() #加一操作前檢查num的值是否小于零
    num += 1
    lock.release()

t=threading.Thread(target=add)
t.start()
t.join()

上面的這段代碼會發(fā)生死鎖情況,因?yàn)樵赼dd函數(shù)里先通過lock.acquire()獲取了鎖严卖,然后調(diào)用check函數(shù)的時(shí)候席舍,check函數(shù)里又在獲取鎖,這就造成死鎖情況哮笆。這種情況也就是在一個(gè)加鎖的操作里調(diào)用另一個(gè)加鎖的方法来颤,而且加的是同一把鎖,就會發(fā)生死鎖稠肘。解決方法很簡單福铅,就是把Lock換成RLock就行了

讓我們在舉一個(gè)實(shí)用點(diǎn)的例子
現(xiàn)在要使用遞歸方法計(jì)算一個(gè)數(shù)的累乘,并要求加鎖實(shí)現(xiàn)計(jì)算過程中不會被其他線程干擾

from threading import Lock

lock = Lock()

def factorial(n):
    assert n > 0
    if n == 1:
        return 1
    
    with lock:       
        out = n * factorial(n - 1)

    return out

print(factorial(3)) #發(fā)生死鎖项阴,無法執(zhí)行

上面遞歸的函數(shù)里使用with 語句簡化了鎖的acquire和release 寫法滑黔。
在with語句里又遞歸調(diào)用了factorial函數(shù)本身,這就會發(fā)生再次請求鎖的情況环揽,所以第一次遞歸的時(shí)候就發(fā)生了死鎖略荡,解決的方法還是一樣,把Lock換成RLock即可

上面的例子說明了多次獲取鎖的實(shí)際用途歉胶,其實(shí)RLock和Lock的區(qū)別還有另一點(diǎn)汛兜,就是:

Lock獲取的鎖可以被其他任何線程直接釋放
RLock獲取的鎖只有獲取這個(gè)鎖的線程自己才能釋放

看下面的例子:

import threading
import time

lock = threading.Lock()

def a():
    lock.acquire()
    time.sleep(3)
    lock.release()
    

def b():
    lock.release()
    print('lock released')


t1=threading.Thread(target=a)
t2=threading.Thread(target=b)

t1.start()
t2.start()

t1.join()
t2.join()

#輸出:
lock released
Exception in thread Thread-1:
Traceback (most recent call last):
  File "D:\python3.6\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "D:\python3.6\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "C:/Users/test/Desktop/test.py", line 9, in a
    lock.release()
RuntimeError: release unlocked lock

t1線程在函數(shù)a里面獲取了鎖,然后sleep 3秒鐘通今,這時(shí)候t2線程的b函數(shù)在沒有獲取鎖的情況下直接就釋放的鎖粥谬,然后執(zhí)行print('lock released')打印輸出,等3秒后線程t1醒來后再試圖釋放一個(gè)已經(jīng)被釋放的鎖的時(shí)候辫塌,就會報(bào)RuntimeError: release unlocked lock 錯(cuò)了帝嗡,說明t2線程可以獲取t1線程的lock鎖

如果把上面的鎖改成RLock,輸出會變成這樣

Exception in thread Thread-2:
Traceback (most recent call last):
  File "D:\python3.6\lib\threading.py", line 916, in _bootstrap_inner
    self.run()
  File "D:\python3.6\lib\threading.py", line 864, in run
    self._target(*self._args, **self._kwargs)
  File "C:/Users/test/Desktop/test.py", line 13, in b
    lock.release()
RuntimeError: cannot release un-acquired lock

報(bào)錯(cuò)信息:無法釋放一個(gè)沒有獲取到的鎖璃氢, 說明t2 線程的b函數(shù)里,無法獲取t1線程獲取的rlock鎖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狮辽,一起剝皮案震驚了整個(gè)濱河市一也,隨后出現(xiàn)的幾起案子巢寡,更是在濱河造成了極大的恐慌,老刑警劉巖椰苟,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抑月,死亡現(xiàn)場離奇詭異,居然都是意外死亡舆蝴,警方通過查閱死者的電腦和手機(jī)谦絮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洁仗,“玉大人层皱,你說我怎么就攤上這事≡剩” “怎么了叫胖?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長她奥。 經(jīng)常有香客問我瓮增,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮潦闲,結(jié)果婚禮上松逊,老公的妹妹穿的比我還像新娘。我一直安慰自己伍俘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著带膜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鸳谜。 梳的紋絲不亂的頭發(fā)上膝藕,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機(jī)與錄音咐扭,去河邊找鬼芭挽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蝗肪,可吹牛的內(nèi)容都是我干的袜爪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼薛闪,長吁一口氣:“原來是場噩夢啊……” “哼辛馆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤昙篙,失蹤者是張志新(化名)和其女友劉穎腊状,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苔可,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缴挖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焚辅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片映屋。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖同蜻,靈堂內(nèi)的尸體忽然破棺而出棚点,到底是詐尸還是另有隱情,我是刑警寧澤埃仪,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布乙濒,位于F島的核電站,受9級特大地震影響卵蛉,放射性物質(zhì)發(fā)生泄漏颁股。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一傻丝、第九天 我趴在偏房一處隱蔽的房頂上張望甘有。 院中可真熱鬧,春花似錦葡缰、人聲如沸亏掀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滤愕。三九已至,卻和暖如春怜校,著一層夾襖步出監(jiān)牢的瞬間间影,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工茄茁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魂贬,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓裙顽,卻偏偏與公主長得像付燥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子愈犹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355