線程與線程之間數據是共享的
from threading import Thread def func(lst, name): lst.append(66) print(name, lst) if __name__ == '__main__': lst = [1, 2] t1 = Thread(target=func, args=(lst, "線程1")) t2 = Thread(target=func, args=(lst, "線程2")) t1.start() t2.start() t1.join() t2.join() print("主線程結束", lst)
執行結果:
線程1 [1, 2, 66] 線程2 [1, 2, 66, 66] 主線程結束 [1, 2, 66, 66]
帶有global的線程與線程之間的數據共享情況
from threading import Thread def func(name): global n print(f"{name}開始,n={n}") n = 0 print(f"{name}結束,n={n}") if __name__ == '__main__': n = 100 t1 = Thread(target=func, args=("線程1",)) t1.start() t1.join() print(f"主線程結束,n={n}")
執行結果:
線程1開始,n=100 線程1結束,n=0 主線程結束,n=0
接下來看一個由於線程之間的數據是共享而引發問題
from threading import Thread def func(name): print(f"{name}開始") global n for i in range(100000): n += 1 print(f"{name}結束,n={n}") if __name__ == '__main__': n = 0 t1 = Thread(target=func, args=("線程1",)) t2 = Thread(target=func, args=("線程2",)) t1.start() t2.start() t1.join() t2.join() print(f"主線程結束,n={n}")
執行結果:
線程1開始 線程2開始 線程1結束,n=165337 線程2結束,n=147914 主線程結束,n=147914
最終的結果不是我們預期的200000
解決線程與線程之間數據共享導致的問題:加鎖
from threading import Thread, Lock def func(name, lock: Lock): # 傳遞lock,后面添加: Lock表示是Lock類型的,是為了上鎖和釋放鎖的時候會有方法提示功能 print(f"{name}開始") global n for i in range(100000): lock.acquire() # 上鎖 n += 1 lock.release() # 釋放鎖 print(f"{name}結束,n={n}") if __name__ == '__main__': lock = Lock() # 創建一把鎖 n = 0 t1 = Thread(target=func, args=("線程1", lock)) t2 = Thread(target=func, args=("線程2", lock)) t1.start() t2.start() t1.join() t2.join() print(f"主線程結束,n={n}")
執行結果:
線程1開始 線程2開始 線程1結束,n=167229 線程2結束,n=200000 主線程結束,n=200000
只看最終結果就行,因為線程1和線程2是並行執行的。
線程與線程之間數據共享(加鎖)
from threading import Thread, Lock n = 0 def func(lock, name): lock.acquire() global n for i in range(1000000): n += 1 print("%s拿到了鎖" % name) lock.release() if __name__ == '__main__': lock = Lock() # 線程鎖 t1 = Thread(target=func, args=(lock, "線程1")) t2 = Thread(target=func, args=(lock, "線程2")) t3 = Thread(target=func, args=(lock, "線程3")) t1.start() t2.start() t3.start() t1.join() t2.join() t3.join() print(n)
運行結果:
線程1拿到了鎖
線程2拿到了鎖
線程3拿到了鎖
3000000
死鎖現象
from threading import Thread, Lock def func(name, lock: Lock): # 傳遞lock,后面添加: Lock表示是Lock類型的,是為了上鎖和釋放鎖的時候會有方法提示功能 print(f"{name}開始") lock.acquire() # 上鎖 lock.acquire() # 上鎖 print(f"{name}被鎖住了") lock.release() # 釋放鎖 lock.release() # 釋放鎖 print(f"{name}結束") if __name__ == '__main__': lock = Lock() # 創建一把鎖 t1 = Thread(target=func, args=("線程1", lock)) t2 = Thread(target=func, args=("線程2", lock)) t1.start() t2.start() t1.join() t2.join() print(f"主線程結束")
執行結果:
線程1開始
線程2開始
程序一直執行不完,因為程序被鎖住了。
解決死鎖--->使用遞歸鎖RLock
from threading import Thread, RLock def func(name, lock: RLock): # 傳遞lock,后面添加: Lock表示是Lock類型的,是為了上鎖和釋放鎖的時候會有方法提示功能 print(f"{name}開始") lock.acquire() # 上鎖 lock.acquire() # 上鎖 print(f"{name}被鎖住了") lock.release() # 釋放鎖 lock.release() # 釋放鎖 print(f"{name}結束") if __name__ == '__main__': lock = RLock() # 創建一把鎖 t1 = Thread(target=func, args=("線程1", lock)) t2 = Thread(target=func, args=("線程2", lock)) t1.start() t2.start() t1.join() t2.join() print(f"主線程結束")
執行結果:
線程1開始
線程1被鎖住了
線程1結束
線程2開始
線程2被鎖住了
線程2結束
主線程結束
完美解決死鎖的問題,切記鎖可以保證數據的安全性,但是效率會降低,相當於把並行改為了串行。
死鎖可以想成兩個人圍着桌子吃飯,兩個人的中間各放着一根的筷子,兩根筷子在一起才能吃飯,此時,兩個人都想吃飯,但是都抓着筷子不放開,結果誰也吃不上飯,就僵死在那了。