【python进阶】_多线程多进程
進程就是運行著的程序
線程就是操作系統創建的,每個線程對應一個代碼執行的數據結構,保存了代碼執行過程中的重要的狀態信息。系統中每個進程里面至少包含一個線程。沒有線程,操作系統沒法管理和維護代碼運行的狀態信息,所以沒有創建線程之前,操縱系統是不會執行我們的代碼的。
現在計算機上面,CPU是多核的,每個核都可以執行代碼。要讓多個CPU核心同時去執行任務,我們的程序必須創建多個進程,讓CPU執行 多個線程 對應的代碼。
python代碼中創建新線程
python3將系統調用創建線程的功能封裝在標準threading中。
print('主線程執行代碼') # 從 threading 庫中導入Thread類 from threading import Thread from time import sleep# 定義一個函數,作為新線程執行的入口函數 def threadFunc(arg1,arg2):print('子線程 開始')print(f'線程函數參數是:{arg1}, {arg2}')sleep(5)print('子線程 結束')# 創建 Thread 類的實例對象 thread = Thread(# target 參數 指定 新線程要執行的函數# 注意,這里指定的函數對象只能寫一個名字,不能后面加括號,# 如果加括號就是直接在當前線程調用執行,而不是在新線程中執行了target=threadFunc, # 如果 新線程函數需要參數,在 args里面填入參數# 注意參數是元組, 如果只有一個參數,后面要有逗號,像這樣 args=('參數1',)args=('參數1', '參數2') )# 執行start 方法,就會創建新線程, # 并且新線程會去執行入口函數里面的代碼。 # 這時候 這個進程 有兩個線程了。 thread.start()# 主線程的代碼執行 子線程對象的join方法, # 就會等待子線程結束,才繼續執行下面的代碼 thread.join() print('主線程結束')創建了一個Thread實例對象,其中,Thread類的初始化參數 有兩個
target參數 是指定新線程的?入口函數, 新線程創建后就會 執行該入口函數里面的代碼,
args 指定了 傳給 入口函數threadFunc 的參數。 線程入口函數 參數,必須放在一個元組里面,里面的元素依次作為入口函數的參數。
注意,上面的代碼只是創建了一個Thread實例對象, 但這時,新的線程還沒有創建。要創建線程,必須要調用 Thread 實例對象的?start方法 。新的線程才創建成功,并開始執行 入口函數threadFunc 里面的代碼
有的時候, 一個線程需要等待其它的線程結束,比如需要根據其他線程運行結束后的結果進行處理。這時可以使用 Thread對象的?join?方法:如果一個線程A的代碼調用了 對應線程B的Thread對象的?join?方法,線程A就會停止繼續執行代碼,等待線程B結束。 線程B結束后,線程A才繼續執行后續的代碼。所以主線程在執行上面的代碼時,就暫停在此處, 一直要等到 新線程執行完畢,退出后,才會繼續執行后續的代碼。
共享數據的訪問控制
from threading import Thread from time import sleepbank = {'byhy' : 0 }# 定義一個函數,作為新線程執行的入口函數 def deposit(theadidx,amount):balance = bank['byhy']# 執行一些任務,耗費了0.1秒sleep(0.1)bank['byhy'] = balance + amountprint(f'子線程 {theadidx} 結束')theadlist = [] for idx in range(10):thread = Thread(target = deposit,args = (idx,1))thread.start()# 把線程對象都存儲到 threadlist中theadlist.append(thread)for thread in theadlist:thread.join()print('主線程結束') print(f'最后我們的賬號余額為 {bank["byhy"]}')在上面代碼中,我們預期的結果是余額為10,但是輸出的結果為1;這是因為多個線程同時調用deposit時,可能出現一個線程覆蓋另一個線程的結果的問題。解決這個問題可以使用threading庫里面的鎖對象Lock去保護,如下:
from time import sleepbank = {'byhy' : 0 }# 定義一個函數,作為新線程執行的入口函數 def deposit(theadidx,amount):balance = bank['byhy']# 執行一些任務,耗費了0.1秒sleep(0.1)bank['byhy'] = balance + amountfor idx in range(10):deposit (idx,1)print(f'最后我們的賬號余額為 {bank["byhy"]}') 代碼都是 串行 執行的。 不存在多線程同時訪問 bank對象 的問題,運行結果一切都是正常的。現在我們程序代碼中,有多個線程,并且在這個幾個線程中都會去調用 deposit,就有可能同時操作這個bank對象,就有可能出一個線程覆蓋另外一個線程的結果的問題。這時,可以使用 threading庫里面的鎖對象 Lock 去保護。我們修改多線程代碼,如下:from threading import Thread,Lock from time import sleepbank = {'byhy' : 0 }bankLock = Lock()# 定義一個函數,作為新線程執行的入口函數 def deposit(theadidx,amount):# 操作共享數據前,申請獲取鎖bankLock.acquire()balance = bank['byhy']# 執行一些任務,耗費了0.1秒sleep(0.1)bank['byhy'] = balance + amountprint(f'子線程 {theadidx} 結束')# 操作完共享數據后,申請釋放鎖bankLock.release()theadlist = [] for idx in range(10):thread = Thread(target = deposit,args = (idx,1))thread.start()# 把線程對象都存儲到 threadlist中theadlist.append(thread)for thread in theadlist:thread.join()print('主線程結束') print(f'最后我們的賬號余額為 {bank["byhy"]}')輸出結果如下:
Lock 對象的acquire方法 是申請鎖。每個線程在 操作共享數據對象之前,都應該 申請獲取操作權,也就是 調用該 共享數據對象對應的鎖對象的acquire方法。如果線程A 執行如下代碼,調用acquire方法的時候,別的線程B 已經申請到了這個鎖, 并且還沒有釋放,那么 線程A的代碼就在此處 等待 線程B 釋放鎖,不去執行后面的代碼。直到線程B 執行了鎖的 release 方法釋放了這個鎖, 線程A 才可以獲取這個鎖,就可以執行下面的代碼了。如果這時線程B 又執行 這個鎖的acquire方法, 就需要等待線程A 執行該鎖對象的release方法釋放鎖, 否則也會等待,不去執行后面的代碼。
Python程序中當所有的 `非daemon線程` 結束了,整個程序才會結束
總結
以上是生活随笔為你收集整理的【python进阶】_多线程多进程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【图像处理opencv】_图像边缘
- 下一篇: 【python进阶】_装饰器