19 - 線程📄目錄1 多任務2 多線程2.1 線程和進程2.2 多線程2.3 多線程的特點2.4 線程之間共享資源2.5 資源競爭3 線程同步3.1 線程等待john()
4 互斥鎖4.1 總結導航連結:
以下看似一起執行,實則有先後次序
xxxxxxxxxx
import time
def sing():
print("I am singing")
time.sleep(2) # 睡眠,以秒為單位
def dance():
print("I am dancing")
time.sleep(2)
print("End!")
sing()
dance()
輸出結果:
xxxxxxxxxx
I am singing
I am dancing
End!
進程:是操作系統進行資源分配的基本單位,每打開一個程序至少就會有一個進程
線程:是CPU調度的基本單位,每一個進程至少都會有一個線程,這個線程通常就是我們所說的主線程
💡 一個進程默認有一個線程,進程裡面可以創建多個線程
💡 線程是依附在進程裡面的,沒有進程就沒有線程
運行時要先導入線程模塊import threading
Thread線程類參數:
target:執行的任務名(要運行的函數不用添加小括號)
args:以元組的形式給任務傳參
kwargs:以字典的形式給任務傳參
xxxxxxxxxx
import threading # 平常應該放到文件開始位置
import time
def sing(name):
print(f'{name} is singing!')
time.sleep(2)
print('He/She is done!')
def hike(name):
print(f'{name} is hiking!')
time.sleep(2)
print('He/She is back!')
# 主程序入口
if __name__ == '__main__':
# 1. 創建子線程
# thread1 = threading.Thread(target=sing) # 不用傳參的例子
thread1 = threading.Thread(target=sing, args=('Victoria',))
# print(thread1) # 打印結果<Thread(Thread-1 (sing), initial)>,代表這是線程對象
thread2 = threading.Thread(target=hike, args=('Antonio',))
# 3. 守護線程,必須放在start()前面。主線程執行結束,子線程也會跟着結束。
thread1.daemon = True # 3.12版本及以後適用,以前是thread1.setDaeman(True)
thread2.daemon = True # ChatGPT稱thread2.Daemon = True(D大寫)是錯誤,應用小寫
# 2. 開啟子線程
thread1.start()
thread2.start()
# 4. 阻塞主線程join():暫停的作用,等子線程執行結束後,主線程才會繼續執行,必須放在start()後面
thread1.join()
thread2.join()
# 6. (可選)更改線程名
thread1.name = '子線程1'
thread2.name = '子線程2'
# 5. (可選)獲取線程名字
print(thread1.name, thread2.name) # 3.12版本及以後適用,以前是print(thread1.getName())
print('Main thread done!')
輸出結果:
xxxxxxxxxx
Victoria is singing!
Antonio is hiking!
He/She is back!
He/She is done!
子線程1 子線程2
Main thread done!
💡 主程序入口下要做的事情:
創建子線程
開啟子線程
守護線程,必須放在start()前面。主線程執行結束,子線程也會跟着結束
阻塞主線程join():暫停的作用,等子線程執行結束後,主線程才會繼續執行,必須放在start()後面
(可選)獲取線程名字
(可選)更改線程名
線程之間執行是無序的
線程執行是根據CPU調度決定的,先調度哪個就執行哪個
xxxxxxxxxx
import threading
import time
def task():
time.sleep(1)
print('當前線程是:', threading.current_thread().name) # 顯示當前線程對象名
if __name__ == '__main__':
for i in range(5):
# 每循環一次就創建一個子線程
thread = threading.Thread(target=task)
# 啟動子線程
thread.start()
輸出結果(每次輸出結果都不一樣,混亂是因為資源競爭):
xxxxxxxxxx
當前線程是:當前線程是: Thread-2 (task)當前線程是:當前線程是: Thread-5 (task)
當前線程是: Thread-4 (task)
Thread-1 (task)Thread-3 (task)
xxxxxxxxxx
import threading
import time
w = 10
li = [] # 定義全局變量
# 寫入數據
def wdata(n):
for i in range(n):
li.append(i)
time.sleep(1)
print("寫入的數據是:", li)
def rdata():
print("讀取的數據是:", li)
if __name__ == "__main__":
# 創建子線程
wd = threading.Thread(target=wdata, args=(w,))
rd = threading.Thread(target=rdata, args=())
# 開啟子線程
wd.start()
wd.join() # 阻塞主線程,在這裡就是讓rd.start()在wd線程完成後才能執行
rd.start()
rd.join()
輸出結果:
xxxxxxxxxx
寫入的數據是: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
讀取的數據是: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
xxxxxxxxxx
a = 0
b = 1000000
def add():
global a
for i in range(b):
global a
a += 1
print('第一次累加:',a)
def add2():
global a
for i in range(b):
global a
a += 1
print('第二次累加:',a)
if __name__ == '__main__':
a1 = threading.Thread(target=add)
a2 = threading.Thread(target=add2)
a1.start()
a2.start()
輸出結果(第一次累加每次輸出結果都不一樣,混亂是因為資源競爭):
xxxxxxxxxx
第一次累加: 1697532
第二次累加: 2000000
主線程和創建的子線程之間各自執行完自己的代碼直至結束
john()
xxxxxxxxxx
a = 0
b = 1000000
def add():
global a
for i in range(b):
global a
a += 1
print('第一次累加:',a)
def add2():
global a
for i in range(b):
global a
a += 1
print('第二次累加:',a)
if __name__ == '__main__':
a1 = threading.Thread(target=add)
a2 = threading.Thread(target=add2)
a1.start()
a1.join() # 等待a1子線程執行結束之後,代碼才繼續往下運行
a2.start()
a2.join()
輸出結果:
xxxxxxxxxx
第一次累加: 1000000
第二次累加: 2000000
概念:對共享數據進行鎖定,保證多個線程訪問共享數據不會出現數據錯誤問題:保證同一時刻只能有一個線程去操作
方法:
acquire()
:上鎖
release()
:釋放鎖
⚠️⚠️注意:這兩個方法必須成對出現,否則會形成死鎖
xxxxxxxxxx
from threading import Lock
# 1. 創建全局互斥鎖
lock = Lock()
a = 0
b = 1000000
def add():
# 2. 上鎖
lock.acquire()
global a
for i in range(b):
global a
a += 1
print('第一次累加:',a)
# 3. 釋放鎖
lock.release()
def add2():
lock.acquire()
global a
for i in range(b):
global a
a += 1
print('第二次累加:',a)
lock.release()
if __name__ == '__main__':
a1 = threading.Thread(target=add)
a2 = threading.Thread(target=add2)
a1.start()
a2.start()
輸出結果:
xxxxxxxxxx
第一次累加: 1000000
第二次累加: 2000000
互斥鎖也是線程同步的一種
互斥鎖的作用:保證同一個時刻只有一個線程去操作共享數據,保證共享數據不會出現錯誤問題
⚠️⚠️上鎖和釋放鎖必須成對出現,否則容易造成「死鎖」現象
⚠️⚠️死鎖:一直等待對方釋放鎖的情況。會造成應用程序停止響應,不能再處理其他任務
❗互斥鎖的缺點:會影響代碼的執行效率
目的地 | 超連結 |
---|---|
首頁 | 返回主頁 |
Python學習 | Python學習 |
上一篇 | 18 - 迭代器&生成器 |
下一篇 | 20 - 進程 |