日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

Python | threading02 - 互斥锁解决多个线程之间随机调度,造成“线程不安全”的问题。

發(fā)布時間:2025/3/15 python 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python | threading02 - 互斥锁解决多个线程之间随机调度,造成“线程不安全”的问题。 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 一、前言
  • 二、線程不安全的現(xiàn)象
    • 2.1、代碼
    • 2.2、運(yùn)行
  • 三、使用互斥鎖解決線程不安全
    • 3.1、代碼
    • 3.2、運(yùn)行
  • 四、忘記釋放互斥鎖,造成死鎖
    • 4.1、代碼
    • 4.2、運(yùn)行
    • 4.3、造成死鎖的一種常見案例
  • 五、with語句拯救粗心的人類
    • 5.1、with語句在互斥鎖上的使用
    • 5.3、運(yùn)行的結(jié)果

一、前言

python多線程與單片機(jī)的RTOS在調(diào)度規(guī)則完全不一樣。python多線程的調(diào)度程序會在任何時候中斷線程(相當(dāng)于調(diào)度,所以python多線程的調(diào)度可以說是很難控制的),單片機(jī)的RTOS會有相應(yīng)的API來產(chǎn)生調(diào)度(調(diào)度是可控的)。

線程與協(xié)程之間的比較還有最后一點(diǎn)要說明:如果使用線程做過重要的編程,就知道寫出程序有多么困難,因?yàn)檎{(diào)度程序任何時候都可能中斷線程。必須記住保留鎖,去保護(hù)程序中的重要部分,防止多步操作在執(zhí)行的過程中被中斷,防止數(shù)據(jù)處于無效狀態(tài)。 —《流暢的Python》- Luciano Ramalho

所以,在我看來:python的協(xié)程與單片機(jī)的RTOS才是相似的。

加粗樣式

多線程的優(yōu)勢在于并發(fā)性,即可以同時運(yùn)行多個任務(wù)。但是當(dāng)線程需要使用共享數(shù)據(jù)時,也可能會由于數(shù)據(jù)不同步產(chǎn)生“錯誤情況”,這是由系統(tǒng)的線程調(diào)度具有一定的隨機(jī)性造成的。

由于線程之間的任務(wù)執(zhí)行是CPU進(jìn)行隨機(jī)調(diào)度的,并且每個線程可能只執(zhí)行了n條指令之后就被切換到別的線程了。當(dāng)多個線程同時操作一個對象,如果沒有很好地保護(hù)該對象,會造成程序結(jié)果的不可預(yù)期,這被稱為“線程不安全”。為了保證數(shù)據(jù)安全,設(shè)計(jì)了線程鎖,即同一時刻只允許一個線程操作該數(shù)據(jù)。

B站有一個視頻講解線程安全問題,個人覺得不錯。

【2021最新版】Python 并發(fā)編程實(shí)戰(zhàn),用多線程、多進(jìn)程、多協(xié)程加速程序運(yùn)行

二、線程不安全的現(xiàn)象


2.1、代碼

代碼的目的是讓number累加到2000000(2百萬),線程counter_1將number累加到1000000,線程counter_2將number累加到2000000。

# python3.9 import threading import timenumber = 0 # lock = threading.Lock()def counter_1():"""子線程1,counter_1"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量# global lock # lock.acquire()for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))# lock.release()def counter_2():"""子線程2,counter_2"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量# global lock# lock.acquire()for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))# lock.release()def main():t1 = threading.Thread(target=counter_1,name="counter_1",daemon=True)t2 = threading.Thread(target=counter_2,name="counter_2",daemon=True)t1.start()t2.start()t1.join()t2.join()print("程序運(yùn)行結(jié)束,number =",number)if __name__ == "__main__":main()

2.2、運(yùn)行

從運(yùn)行的四次結(jié)果看來,每一次的結(jié)果都不一樣,而且都沒有出現(xiàn)一次2000000。這個現(xiàn)象就是因?yàn)槎鄠€線程同時訪問一個對象所造成的線程不安全問題。

三、使用互斥鎖解決線程不安全


3.1、代碼

# python3.9 import threading import timenumber = 0 lock = threading.Lock() # 互斥鎖def counter_1():"""子線程1,counter_1"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量 lock.acquire() # 獲取互斥鎖for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))lock.release() # 釋放互斥鎖(千萬記得用完要釋放,否則會出現(xiàn)死鎖)def counter_2():"""子線程2,counter_2"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量lock.acquire() # 獲取互斥鎖for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))lock.release() # 釋放互斥鎖(千萬記得用完要釋放,否則會出現(xiàn)死鎖)def main():t1 = threading.Thread(target=counter_1,name="counter_1",daemon=True) #創(chuàng)建線程counter_1t2 = threading.Thread(target=counter_2,name="counter_2",daemon=True) #創(chuàng)建線程counter_2t1.start() # 啟動線程counter_1t2.start() # 啟動線程counter_2t1.join() # 阻塞主線程,等待線程counter_1運(yùn)行結(jié)束t2.join() # 阻塞主線程,等待線程counter_2運(yùn)行結(jié)束print("程序運(yùn)行結(jié)束,number =",number)if __name__ == "__main__":main()

3.2、運(yùn)行

四次運(yùn)行的結(jié)果都符合預(yù)期的2000000,表示互斥鎖解決了線程不安全的問題。但是,獲取互斥鎖并使用完之后一定,一定,一定要釋放互斥鎖。否則,會出現(xiàn)死鎖的問題。(其他獲取互斥鎖的線程將一直阻塞在那里。)

四、忘記釋放互斥鎖,造成死鎖


4.1、代碼

# python3.9 import threading import timenumber = 0 lock = threading.Lock() # 互斥鎖def counter_1():"""子線程1,counter_1"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量 lock.acquire() # 獲取互斥鎖for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))# lock.release() # 釋放互斥鎖(千萬記得用完要釋放,否則會出現(xiàn)死鎖)def counter_2():"""子線程2,counter_2"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量lock.acquire() # 獲取互斥鎖for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))lock.release() # 釋放互斥鎖(千萬記得用完要釋放,否則會出現(xiàn)死鎖)def main():t1 = threading.Thread(target=counter_1,name="counter_1",daemon=True) #創(chuàng)建線程counter_1t2 = threading.Thread(target=counter_2,name="counter_2",daemon=True) #創(chuàng)建線程counter_2t1.start() # 啟動線程counter_1t2.start() # 啟動線程counter_2t1.join() # 阻塞主線程,等待線程counter_1運(yùn)行結(jié)束t2.join() # 阻塞主線程,等待線程counter_2運(yùn)行結(jié)束print("程序運(yùn)行結(jié)束,number =",number)if __name__ == "__main__":main()

在這里我故意忘記將獲取的互斥鎖釋放回去了,會出現(xiàn)什么問題?

4.2、運(yùn)行


程序卡死在哪里??我在代碼上加入一句代碼看看程序是不是真的卡在線程counter_2里。

運(yùn)行代碼看看。

運(yùn)行的結(jié)果證明了線程counter_2被阻塞在lock.acquire( )代碼這里,并一直一直阻塞下去,造成整個python程序卡死。

所以,緊記要釋放互斥鎖。

4.3、造成死鎖的一種常見案例

為了簡化問題,我們設(shè)有兩個并發(fā)的線程( 線程A線程B ),需要 資源1資源2 .假設(shè) 線程A 需要 資源1線程B 需要 資源2 .在這種情況下,兩個線程都使用各自的鎖,目前為止沒有沖突。現(xiàn)在假設(shè),在雙方釋放鎖之前, 線程A 需要 資源2 的鎖, 線程B 需要 資源1 的鎖,沒有資源線程不會繼續(xù)執(zhí)行。鑒于目前兩個資源的鎖都是被占用的,而且在對方的鎖釋放之前都處于等待且不釋放鎖的狀態(tài)。這是死鎖的典型情況。所以如上所說,使用鎖來解決同步問題是一個可行卻存在潛在問題的方案。 —摘自《python并行編程中文版》

五、with語句拯救粗心的人類


5.1、with語句在互斥鎖上的使用

# python3.9 import threading import timenumber = 0 lock = threading.Lock() # 互斥鎖def counter_1():"""子線程1,counter_1"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量# 使用with語句管理互斥鎖 with lock:# 鎖的對象for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))def counter_2():"""子線程2,counter_2"""global number # 聲明number是全局變量,并不是函數(shù)的局部變量global lock # 聲明lock是全局變量,并不是函數(shù)的局部變量# 使用with語句管理互斥鎖with lock:# 鎖的對象for i in range(1000000):number +=1print("子線程%s運(yùn)算結(jié)束后,number = %s" % (threading.current_thread().getName(),number))def main():t1 = threading.Thread(target=counter_1,name="counter_1",daemon=True) #創(chuàng)建線程counter_1t2 = threading.Thread(target=counter_2,name="counter_2",daemon=True) #創(chuàng)建線程counter_2t1.start() # 啟動線程counter_1t2.start() # 啟動線程counter_2t1.join() # 阻塞主線程,等待線程counter_1運(yùn)行結(jié)束t2.join() # 阻塞主線程,等待線程counter_2運(yùn)行結(jié)束print("程序運(yùn)行結(jié)束,number =",number)if __name__ == "__main__":main()

5.3、運(yùn)行的結(jié)果

從運(yùn)行的結(jié)果看來并沒有出現(xiàn)線程不安全問題。

with語句真?zhèn)ゴ笥趾啙?#xff01;!!!

總結(jié)

以上是生活随笔為你收集整理的Python | threading02 - 互斥锁解决多个线程之间随机调度,造成“线程不安全”的问题。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 干美女少妇 | 亚洲性欧美| 自拍偷拍欧美视频 | 亚洲天天综合 | 欧美高清性xxxx | 国产三区在线播放 | 日韩视频免费在线 | www.色悠悠| 玩弄丰满少妇xxxxx性多毛 | 亚洲国产av一区二区三区 | 欧美三日本三级少妇三级99观看视频 | 婷婷天堂| 日韩免费观看av | 毛片一卡二卡 | 一级黄色大片视频 | 永久免费看成人av的动态图 | 久久99久久99精品蜜柚传媒 | 韩国美女福利视频 | 国内外成人激情视频 | 国产视频一区二区三区四区五区 | 久久午夜鲁丝片午夜精品 | 波多野结衣视频免费在线观看 | 东京av男人的天堂 | 亚洲男人天堂2019 | 久久高清免费视频 | 狠狠干精品 | 麻豆网站视频 | 国产日产精品一区二区三区四区 | 黑人精品无码一区二区三区 | 日本天堂在线播放 | 中文字幕一区二区在线观看 | 大度亲吻原声视频在线观看 | 国产精品v欧美精品v日韩精品 | 色撸撸在线 | 久久久久久久久久久久久久久久久 | 日本三级456 | 欧美成人a∨高清免费观看 国产精品999视频 | 国产ts系列 | 91福利视频网站 | wwwxxx日本免费 | 亚洲精品久久久久久国产精华液 | 一级片a级片| 久久久综合视频 | 边啃奶头边躁狠狠躁 | 一本久久久 | 女性向片在线观看 | 国产日韩一区二区在线 | 成年人免费网址 | 无遮挡的裸体按摩的视频 | 午夜免费视频网站 | 国产性猛交普通话对白 | 欧美视频一区二区在线观看 | 久久三级视频 | 好吊视频一区二区三区四区 | 国产夫妻在线 | 成人免费视频国产免费网站 | 黄色一级大片在线免费看国产 | 成年人在线免费看 | 非洲黑寡妇性猛交视频 | 人妻 丝袜美腿 中文字幕 | 中文字幕日韩在线观看 | 全程偷拍露脸中年夫妇 | 日日夜夜网 | 午夜福利啪啪片 | 欧美日韩首页 | 久久伊人热 | 国产午夜福利精品 | 午夜刺激视频 | 国产精品日韩在线观看 | 久久精品国产精品亚洲色婷婷 | 亚洲AV无码一区二区伊人久久 | av加勒比在线 | 亚洲婷婷久久综合 | 国产精品入口日韩视频大尺度 | 在线观看国产区 | 国产少妇在线 | 中文字幕人妻一区二区三区视频 | 可以看污的网站 | 欧美1级片 | 婷婷亚洲综合 | 亚洲av成人无码久久精品老人 | 一区二区三区视频观看 | 国产91免费在线观看 | 免费成人深夜夜行网站视频 | 性欧美巨大乳 | 亚洲欧美第一 | 日本不卡一区二区三区 | 日本午夜精品理论片a级app发布 | 国产精品美女一区二区三区 | 二区三区av | 免费福利视频网站 | 欧美a√在线| 人人操日日干 | 天天做夜夜操 | 真人一及毛片 | 在线污视频 | 欧美激情18 | 国产女人18水真多毛片18精品 | 在线观看国产一区二区 |