python3線程同步装处,Lock离钝、Rlock方咆、Condition方式

線程同步

Lock、Rlock鎖機(jī)制

使用鎖的原因

為了避免線程間進(jìn)行數(shù)據(jù)競(jìng)爭(zhēng)蟀架,有時(shí)必須使用一些機(jī)制來(lái)強(qiáng)制線程同步瓣赂。因?yàn)镃python解釋器中GIL(全局解釋鎖)的存在,在每一時(shí)刻只有一個(gè)線程在CPU中執(zhí)行片拍,每個(gè)線程執(zhí)行了一定數(shù)量的字節(jié)碼或者過(guò)了一定的時(shí)間切片再或者遇到了IO操作煌集,CPU就會(huì)切換其他線程執(zhí)行部分字節(jié)碼。那么如果使用兩個(gè)線程分別執(zhí)行add()捌省、reduce()方法苫纤,在理論情況下最后的結(jié)果num可能是0、1纲缓、-1卷拘,因?yàn)榇a分解為字節(jié)碼時(shí),就分解成了很多步驟祝高,有可能某個(gè)線程只執(zhí)行了一些字節(jié)碼栗弟,又去執(zhí)行另一個(gè)線程的字節(jié)碼,這樣就可能造成結(jié)果的錯(cuò)亂工闺。

import dis 

num = 0

def add(num):
    num += 1
    return num 

def reduce(num):
    num -= 1
    return num
print(dis.dis(add))
print(dis.dis(reduce))

下面就是add()乍赫、reduce()方法執(zhí)行的字節(jié)碼,在極端情況下陆蟆,如果add雷厂、reduce每執(zhí)行一個(gè)字節(jié)碼就切換一次,那么結(jié)果就不0叠殷,就是1或者-1

  5           0 LOAD_FAST         0 (num)  加載變量num到內(nèi)存
              2 LOAD_CONST        1 (1)    加載變量1到內(nèi)存
              4 INPLACE_ADD                執(zhí)行加法
              6 STORE_FAST        0 (num)  將結(jié)果賦值給num

  6           8 LOAD_FAST         0 (num)
             10 RETURN_VALUE      None
             
 10           0 LOAD_FAST                0 (num)
              2 LOAD_CONST               1 (1)
              4 INPLACE_SUBTRACT
              6 STORE_FAST               0 (num)

 11           8 LOAD_FAST                0 (num)
             10 RETURN_VALUE             None

使用鎖的方式

為了避免上面出現(xiàn)的情況改鲫,必須給某些代碼段加上鎖

import threading

num = 0


def add(lock):
    global num
    for i in range(1000000):
        lock.acquire()
        num += 1
        lock.release()


def reduce(lock):
    global num
    for i in range(1000000):
        lock.acquire()
        num -= 1
        lock.release()


# 創(chuàng)建鎖
lock = threading.Lock()
# 創(chuàng)建線程
t1 = threading.Thread(target=add, args=(lock, ))
t2 = threading.Thread(target=reduce, args=(lock, ))
t1.start()
t2.start()
# 等待子進(jìn)程執(zhí)行完
t1.join()
t2.join()
print(num)

這樣結(jié)果就是0,但是加鎖雖然達(dá)到了我們期望的結(jié)果林束,但是執(zhí)行程序花費(fèi)的時(shí)間卻長(zhǎng)了钩杰。

可重入的鎖

在一個(gè)線程中,我們?cè)诓糠执a中使用了鎖lock.acquire()還沒(méi)有釋放鎖時(shí)诊县,調(diào)用了另一個(gè)方法讲弄,方法中又使用了鎖,這樣如果使用同一把普通的鎖很容易出現(xiàn)死鎖依痊。這時(shí)我們可以使用Rlock避除,但是要注意使用了幾次Rlock.acquire()怎披,就必須使用幾次Rlock.release(),下面是示例代碼

import threading

num = 0


def add(rlock):
    global num
    for i in range(1000000):
        rlock.acquire()
        num += 1
        dosomething(rlock)
        rlock.release()


def reduce(rlock):
    global num
    for i in range(1000000):
        rlock.acquire()
        num -= 1
        dosomething(rlock)
        rlock.release()


def dosomething(rlock):
    # 模擬其他操作的函數(shù)
    rlock.acquire()
    # dosomething
    rlock.release()


# 創(chuàng)建鎖
rlock = threading.RLock()
# 創(chuàng)建線程
t1 = threading.Thread(target=add, args=(rlock,))
t2 = threading.Thread(target=reduce, args=(rlock,))
t1.start()
t2.start()
# 等待子進(jìn)程執(zhí)行完
t1.join()
t2.join()
print(num)

使用鎖的壞處

因?yàn)閯?chuàng)建鎖解鎖都要花費(fèi)時(shí)間瓶摆,會(huì)影響程序性能凉逛;并且在使用鎖時(shí),容易產(chǎn)生死鎖群井。

產(chǎn)生死鎖的方式:1.創(chuàng)建鎖状飞,沒(méi)有解鎖 ; 2.兩個(gè)線程互相有對(duì)方的鎖,然后互相等待书斜。

Condition機(jī)制

條件同步诬辈,用戶復(fù)雜的線程間同步。內(nèi)部使用的也是lock或者Rlock

重要的方法

  1. wait(timeout=None)

    當(dāng)前線程等待著另外的線程發(fā)起通知荐吉,才向下執(zhí)行焙糟。

  2. notify()

    發(fā)出通知

示例代碼

需要了解的知識(shí)都在代碼中的注釋

import threading

"""
張三:床前明月光
李四:疑是地上霜
張三:舉頭望明月
李四:低頭思故鄉(xiāng)

怎么實(shí)現(xiàn)兩個(gè)線程的交替說(shuō)話,如果只有兩句样屠,可以使用鎖機(jī)制穿撮,讓某個(gè)線程先執(zhí)行,這里有多句話交替出現(xiàn)痪欲,最好使用condition
"""


class ZSThead(threading.Thread):
    def __init__(self, name, cond):
        super(ZSThead, self).__init__()
        self.name = name
        self.cond = cond

    def run(self):
        # 必須先調(diào)用with self.cond悦穿,才能使用wait()、notify()方法
        with self.cond:
            # 講話
            print("{}:床前明月光".format(self.name))
            # 等待李四的回應(yīng)
            self.cond.notify()
            self.cond.wait()

            # 講話
            print("{}:舉頭望明月".format(self.name))
            # 等待李四的回應(yīng)
            self.cond.notify()
            self.cond.wait()


class LSThread(threading.Thread):
    def __init__(self, name, cond):
        super(LSThread, self).__init__()
        self.name = name
        self.cond = cond

    def run(self):
        with self.cond:
            # wait()方法不僅能獲得一把鎖业踢,并且能夠釋放cond的大鎖咧党,這樣張三才能進(jìn)入with self.cond中
            self.cond.wait()
            print(f"{self.name}:疑是地上霜")
            # notify()釋放wait()生成的鎖
            self.cond.notify()

            self.cond.wait()
            print(f"{self.name}:低頭思故鄉(xiāng)")
            self.cond.notify()


cond = threading.Condition()
zs = ZSThead("張三", cond)
ls = LSThread("李四", cond)

# 啟動(dòng)順序很重要,必須先啟動(dòng)李四陨亡,讓他在那里等待著傍衡,
# 因?yàn)橄葐?dòng)張三時(shí),他說(shuō)了話就發(fā)出了通知负蠕,但是當(dāng)時(shí)李四的進(jìn)程還沒(méi)有啟動(dòng)蛙埂,
# 并且condition外面的大鎖也沒(méi)有釋放,李四也沒(méi)法獲取self.cond這把大鎖
# condition有兩層鎖遮糖,一把底層鎖在線程調(diào)用了wait()方法就會(huì)釋放
# 每次調(diào)用wait()方法后绣的,都會(huì)創(chuàng)建一把鎖放進(jìn)condition的雙向隊(duì)列中,等待notify()方法的喚醒
ls.start()
zs.start()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欲账,一起剝皮案震驚了整個(gè)濱河市屡江,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赛不,老刑警劉巖惩嘉,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異踢故,居然都是意外死亡文黎,警方通過(guò)查閱死者的電腦和手機(jī)惹苗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)耸峭,“玉大人桩蓉,你說(shuō)我怎么就攤上這事±湍郑” “怎么了院究?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)本涕。 經(jīng)常有香客問(wèn)我业汰,道長(zhǎng),這世上最難降的妖魔是什么偏友? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任蔬胯,我火速辦了婚禮对供,結(jié)果婚禮上位他,老公的妹妹穿的比我還像新娘。我一直安慰自己产场,他們只是感情好鹅髓,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著京景,像睡著了一般窿冯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上确徙,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天醒串,我揣著相機(jī)與錄音,去河邊找鬼鄙皇。 笑死芜赌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伴逸。 我是一名探鬼主播缠沈,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼错蝴!你這毒婦竟也來(lái)了洲愤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤顷锰,失蹤者是張志新(化名)和其女友劉穎柬赐,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體官紫,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躺率,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年玛界,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悼吱。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡慎框,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出后添,到底是詐尸還是另有隱情笨枯,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布遇西,位于F島的核電站馅精,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏粱檀。R本人自食惡果不足惜洲敢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茄蚯。 院中可真熱鬧压彭,春花似錦、人聲如沸渗常。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)皱碘。三九已至询一,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間癌椿,已是汗流浹背健蕊。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踢俄,地道東北人缩功。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像褪贵,于是被迫代替她去往敵國(guó)和親掂之。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書(shū)筆記脆丁,整理的知識(shí)點(diǎn)世舰,也是為了防止忘記,尊重勞動(dòng)成果槽卫,轉(zhuǎn)載注明出處哦跟压!如果你也喜歡,那...
    波波波先森閱讀 11,242評(píng)論 4 56
  • 多線程三個(gè)特征:原子性查剖、可見(jiàn)性以及有序性. 同步鎖 /并發(fā)鎖/ 讀寫(xiě)鎖钾虐,顯示鎖, ReentrantLock與Co...
    架構(gòu)師springboot閱讀 1,913評(píng)論 0 5
  • 感恩宇宙萬(wàn)物的滋養(yǎng)笋庄! 感恩天地國(guó)家的護(hù)佑效扫! 感恩祖宗父母的血脈傳承與教導(dǎo)! 感恩古圣先賢的大愛(ài)與智慧直砂! 感恩生命可...
    涵曦涵閱讀 192評(píng)論 0 0
  • 1菌仁、 那年9月的某天,經(jīng)友人介紹你加我微信静暂,于是济丘,我們有一搭沒(méi)一搭的聊了,還沒(méi)見(jiàn)面洽蛀,屏幕的兩端都是模糊的摹迷,只...
    楚緣水中蝶閱讀 1,064評(píng)論 9 8
  • 突然發(fā)現(xiàn)每次做一件事時(shí)總是把自己搞的時(shí)間特別緊張~分析下原~ 1.事情第一次做,心里沒(méi)底 2.做前沒(méi)有計(jì)劃 3.做...
    插畫(huà)姐閱讀 300評(píng)論 2 3