淺談python多線程和多線程變量共享問(wèn)題介紹
1、demo
第一個(gè)代碼是多線程的簡(jiǎn)單使用,編寫了線程如何執(zhí)行函數(shù)和類。
import threading import time class ClassName(threading.Thread): """創(chuàng)建類,通過(guò)多線程執(zhí)行""" def run(self): for i in range(5): print(i) time.sleep(1) def sing(): for i in range(1,11): print("唱歌第 %d 遍" % i) time.sleep(1) def dance(): for i in range(1,16): print("跳舞第 %d 遍" % i) time.sleep(1) def main(): t1 = threading.Thread(target = sing) t2 = threading.Thread(target = dance) t = ClassName() # 啟動(dòng)線程 t1.start() t2.start() t.start() while True: length = len(threading.enumerate()) print("正在運(yùn)行的線程有 %s" %threading.enumerate()) if length <= 1: break time.sleep(1) if __name__ == '__main__': main()
執(zhí)行結(jié)果可以看到函數(shù) sing、dance和類在同時(shí)執(zhí)行,執(zhí)行效果太長(zhǎng)就不方截圖了
2、多線程共享變量
通過(guò)定義全局變量,然后再test1函數(shù)類部進(jìn)行更改全局變量,test2打印全局變量。
import threading import time #定義全局變量 g_num = 0 def test1(): """函數(shù)test1對(duì)全局變量進(jìn)行更改""" global g_num for i in range(1,10): g_num += 1 print("--- test1 線程 g_num = %d--- " % g_num) def test2(): """函數(shù)test2 打印全局變量""" print("--- test2 線程 g_num = %d--- " % g_num) def main(): t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) # 啟動(dòng)線程 t1.start() # 增加睡眠是為了保證優(yōu)先執(zhí)行函數(shù)test1 time.sleep(1) t2.start() print("--- 主線程 g_num = %d--- " % g_num) if __name__ == '__main__': main()
執(zhí)行結(jié)果可以看出,在主線程和創(chuàng)建的兩個(gè)線程中讀取的是一樣的值,既可以表明在多線程中變量共享
3、資源競(jìng)爭(zhēng)
在多線程兩個(gè)函數(shù)中同時(shí)更改一個(gè)變量時(shí),由于cpu的計(jì)算能力,當(dāng)修改參數(shù)的代碼塊無(wú)法一次性執(zhí)行完成時(shí),就會(huì)產(chǎn)生資源競(jìng)爭(zhēng)
import threading import time # 定義全局變量 g_num = 0 def test1(num): """函數(shù)test1對(duì)全局變量進(jìn)行更改""" global g_num for i in range(num): g_num += 1 print("test1 線程 g_num = %d---" % g_num) def test2(num): """函數(shù)test2對(duì)全局變量進(jìn)行更改""" global g_num for i in range(num): g_num += 1 print("tes2 線程 g_num = %d---" % g_num) def main(): t1 = threading.Thread(target=test1, args=(1000000, )) t2 = threading.Thread(target=test2, args=(1000000, )) t1.start() t2.start() time.sleep(1) print("主線程 g_num = %d---" % g_num) if __name__ == '__main__': main()
可以先試試傳遞參數(shù)為100時(shí),可以看到g_num = 200 這是因?yàn)楹瘮?shù)代碼可以一次性執(zhí)行完成,當(dāng)參數(shù)為1000000時(shí)代碼無(wú)法一次性執(zhí)行完成,g_num!= 2000000
4、互斥鎖
互斥鎖可以解決資源競(jìng)爭(zhēng)的問(wèn)題,原理很簡(jiǎn)單,通過(guò)對(duì)代碼塊上鎖,保證該代碼執(zhí)行完成前,其它代碼無(wú)法進(jìn)行修改。執(zhí)行完成后解鎖,其它代碼就可以執(zhí)行了。
import threading import time # 創(chuàng)建變量 g_num = 0 # 創(chuàng)建鎖默認(rèn)為開(kāi)鎖狀態(tài) mutex = threading.Lock() def test1(num): global g_num for i in range(num): # 上鎖 mutex.acquire() g_num += 1 # 解鎖 mutex.release() print("--- test1 線程 g_num = %d---" % g_num) def test2(num): global g_num for i in range(num): # 上鎖 mutex.acquire() g_num += 1 # 解鎖 mutex.release() print("--- test2 線程 g_num = %d---" % g_num) def main(): t1 = threading.Thread(target=test1, args=(1000000, )) t2 = threading.Thread(target=test2, args=(1000000, )) t1.start() t2.start() time.sleep(1) print("--- 主線程 g_num = %d---" % g_num) if __name__ == '__main__': main()
可以看到加了鎖之后,代碼執(zhí)行不會(huì)出現(xiàn)資源競(jìng)爭(zhēng),結(jié)果也是正常的?;コ怄i,上鎖的代碼越少越好。
5、死鎖
當(dāng)出現(xiàn)多個(gè)鎖時(shí),就可能會(huì)產(chǎn)生死鎖這個(gè)情況。當(dāng)關(guān)閉一個(gè)鎖時(shí),這個(gè)鎖已經(jīng)為關(guān)閉狀態(tài)的話,程序就會(huì)阻塞。就如同下面這個(gè)代碼中。函數(shù)test1關(guān)閉mutexB鎖時(shí),函數(shù)test2提前將其關(guān)閉了,未進(jìn)行解鎖,程序就會(huì)一直阻塞。
import threading import time # 創(chuàng)建兩個(gè)鎖A, B mutexA = threading.Lock() mutexB = threading.Lock() def test1(): # 對(duì)muctexA上鎖 mutexA.acquire() # mutexA上鎖后,延時(shí)1秒,等待mutexB上鎖 print("test1 ---do1---up---") time.sleep(1) # 此時(shí)會(huì)堵塞,因?yàn)閙utexB已經(jīng)上鎖 mutexB.acquire() print("test1 ---do1---down---") mutexB.release() # 對(duì)mutexA解鎖 mutexA.release() def test2(): # 對(duì)muctexB上鎖 mutexB.acquire() # mutexB上鎖后,延時(shí)1秒,等待mutexA上鎖 print("test2 ---do1---up---") time.sleep(1) # 此時(shí)會(huì)堵塞,因?yàn)閙utexB已經(jīng)上鎖 mutexA.acquire() print("test2 ---do1---down---") mutexA.release() # 對(duì)mutexA解鎖 mutexB.release() def main(): t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start() if __name__ == '__main__': main()
代碼執(zhí)行效果可以看到程序會(huì)一直阻塞
解決方法
1、在程序編寫時(shí),就需要注意避免死鎖
2、可以參考銀行家算法
到此這篇關(guān)于淺談python多線程和多線程變量共享問(wèn)題介紹的文章就介紹到這了,更多相關(guān)python 多線程變量共享內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python+playwright微軟自動(dòng)化工具的使用
這篇文章主要介紹了python+playwright微軟自動(dòng)化工具的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02在Python中使用循環(huán)進(jìn)行迭代的方法小結(jié)
Python中的循環(huán)結(jié)構(gòu)是編程中的重要組成部分,本文詳細(xì)介紹這兩種循環(huán)的使用方法、它們之間的差異以及如何選擇合適的循環(huán)類型,此外,我還將介紹一些高級(jí)循環(huán)控制技巧,如列表推導(dǎo)式和生成器表達(dá)式,感興趣的朋友一起看看吧2024-01-01Python使用POP3和SMTP協(xié)議收發(fā)郵件的示例代碼
這篇文章主要介紹了Python使用POP3和SMTP協(xié)議收發(fā)郵件的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Python爬蟲(chóng)之超級(jí)鷹驗(yàn)證碼應(yīng)用
眾所周知python是一個(gè)很強(qiáng)大的語(yǔ)言,它擁有眾多的庫(kù),今天我嘗試了使用超級(jí)鷹第三方平臺(tái)進(jìn)行驗(yàn)證碼的開(kāi)發(fā),需要的朋友可以參考下2022-08-08jupyter運(yùn)行時(shí)左邊一直出現(xiàn)*號(hào)問(wèn)題及解決
這篇文章主要介紹了jupyter運(yùn)行時(shí)左邊一直出現(xiàn)*號(hào)問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Python 經(jīng)典貪心算法之Prim算法案例詳解
這篇文章主要介紹了Python 經(jīng)典貪心算法之Prim算法案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09Python裝飾器原理與簡(jiǎn)單用法實(shí)例分析
這篇文章主要介紹了Python裝飾器原理與簡(jiǎn)單用法,結(jié)合實(shí)例形式分析了Python裝飾器的概念、原理、使用方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2018-04-04自己搭建resnet18網(wǎng)絡(luò)并加載torchvision自帶權(quán)重的操作
這篇文章主要介紹了自己搭建resnet18網(wǎng)絡(luò)并加載torchvision自帶權(quán)重的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05